std::call_once
https://en.cppreference.com/w/cpp/thread/call_once
确保以线程安全的方式调用callable一次。
由于还有其他方法可以做类似的事情,问题是:
什么时候应该使用?它的目的是什么类型的问题?
请提供示例。
示例:我将其用于libcURL以从网站检索http(s)数据。在libcURL中,您必须先使用one-time global initialization才能使用该库。鉴于初始化不是线程安全的,但是从网站请求数据是线程安全的,我使用call_once
只调用我的初始化一次,无论是什么线程以及是否同时调用它。
典型的用途是在需要争用(多线程)的情况下按需初始化全局数据。
假设你有结构
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
版本。
想象一个包含一些巨型数据的单例实例(出于某种原因):
class Singleton {
public: static Singleton& get();
...
private: static std::unique_ptr<SingletonDataBase> instance;
}
我们怎样才能确保get函数在被正确调用时创建实例(无论出于何种原因它实际上很大并且不能进入静态内存空间)。我们如何实现这一目标?
mutex
?我觉得有点难看。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
了。
什么时候应该使用?
当你想要打电话一次。它的内容简明扼要。
替代方案
struct CallFooOnce {
CallFooOnce() {
foo();
}
};
static CallFooOnce foo_once;
有更多的样板,并引入了一个额外的名称
static std::once_flag foo_once;
std::call_once(foo_once, foo);