std::call_once
https://en.cppreference.com/w/cpp/thread/call_once
确保以线程安全的方式恰好一次调用了callable。
由于还有其他类似的方法,问题是:
何时应使用?它打算解决什么类型的问题?
请提供示例。
答案 0 :(得分:8)
示例:我将它用于libcURL来从网站检索http(s)数据。在libcURL中,必须使用one-time global initialization才能使用该库。鉴于初始化不是线程安全的,但是从网站请求数据是线程安全的,所以我使用call_once
,它只调用一次初始化,无论在哪个线程中以及是否同时调用
答案 1 :(得分: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
版本。
答案 2 :(得分:1)
想象一个单例实例,其中包含一些巨大的数据(由于某种原因):
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
很高兴。
答案 3 :(得分:0)
何时应使用?
您想打一次电话。简洁明了。
替代
struct CallFooOnce {
CallFooOnce() {
foo();
}
};
static CallFooOnce foo_once;
具有更多样板,并在上方加上另一个名称
static std::once_flag foo_once;
std::call_once(foo_once, foo);
答案 4 :(得分:0)
缓存和惰性求值。假设一个不可变类有一个存储成本低但计算成本高的属性,double foo() const;
。与其按需计算或预先计算,不如添加 mutable std::once_flag m_flag; mutable double m_foo;
然后可以执行
double foo() const {
std::call_once(m_flag, [this] { m_foo = doCalcFoo(); });
return m_foo;
}
答案 5 :(得分:-1)
std::call_once()
可用于惰性求值,传递给它的可调用对象即使有多个线程执行,也只会执行一次。请参见下一个示例,其中方法 getInstance()
可能会被多个线程调用多次,但是该实例仅在需要时创建一次(第一次调用 getInstance())。< /p>
#include <mutex>
class Singleton {
static Singleton *instance;
static std::once_flag inited;
Singleton() {...} //private
public:
static Singleton *getInstance() {
std::call_once( inited, []() {
instance=new Singleton();
});
return instance;
}
};