std :: call_once,什么时候应该使用?

问题描述 投票:7回答:4

std::call_once https://en.cppreference.com/w/cpp/thread/call_once

确保以线程安全的方式调用callable一次。

由于还有其他方法可以做类似的事情,问题是:

什么时候应该使用?它的目的是什么类型的问题?

请提供示例。

c++ c++17
4个回答
8
投票

示例:我将其用于libcURL以从网站检索http(s)数据。在libcURL中,您必须先使用one-time global initialization才能使用该库。鉴于初始化不是线程安全的,但是从网站请求数据是线程安全的,我使用call_once只调用我的初始化一次,无论是什么线程以及是否同时调用它。


2
投票

典型的用途是在需要争用(多线程)的情况下按需初始化全局数据。

假设你有结构

struct A{ A() {/*do some stuff*/} };

并且您想在全局范围内使用它的实例。

如果你这样做,它会在main之前初始化,所以它不是按需的。

A a_global;

如果你这样做,那么它是按需的,但它不是线程安全的。

A *a_singleton = NULL;
A *getA() { 
   if (!a_singleton)
      a_singleton = new A();
   return a_singleton;
}

call_once解决了这两个问题。当然,您可以使用其他同步原语的某种组合,但您最终会重新实现自己的call_once版本。


1
投票

想象一个包含一些巨型数据的单例实例(出于某种原因):

class Singleton {
    public:  static Singleton& get();
    ...
    private: static std::unique_ptr<SingletonDataBase> instance;
}

我们怎样才能确保get函数在被正确调用时创建实例(无论出于何种原因它实际上很大并且不能进入静态内存空间)。我们如何实现这一目标?

  1. 使用mutex?我觉得有点难看。
  2. 使用std::call_once?更好,并坚定地给出了代码的意图:

Singleton& Singleton::get() {
    static std::once_flag flag;
    std::call_once(flag, [&](){ instance.reset(new SingletonDataBase()); });
    return instance.get_interface()
}

无论何时你只需要调用一次,就可以使用call_once了。


0
投票

什么时候应该使用?

当你想要打电话一次。它的内容简明扼要。

替代方案

struct CallFooOnce { 
    CallFooOnce() { 
        foo(); 
    } 
}; 
static CallFooOnce foo_once;

有更多的样板,并引入了一个额外的名称

static std::once_flag foo_once;
std::call_once(foo_once, foo);
© www.soinside.com 2019 - 2024. All rights reserved.