std::call_once https://en.cppreference.com/w/cpp/thread/call_once
确保以线程安全的方式恰好一次调用了callable。
由于还有其他类似的方法,因此问题是:
什么时候应该使用?它打算解决什么类型的问题?
请提供示例。
示例:我将它用于libcURL来从网站检索http(s)数据。在libcURL中,必须先进行一次全局初始化,然后才能使用该库。鉴于初始化不是线程安全的,但是从网站请求数据是线程安全的call_once,因此无论在哪个线程中以及是否同时调用它,我都只调用一次初始化。
典型用途是当您希望在可能发生争用(多线程)的情况下按需初始化全局数据。
假设你有结构
struct A{ A() {/*do some stuff*/} };
Run Code Online (Sandbox Code Playgroud)
并且你想要一个在全局范围内的实例。
如果您按如下方式进行,它会在 main 之前初始化,因此它不是按需的。
A a_global;
Run Code Online (Sandbox Code Playgroud)
如果您按照以下方式进行操作,则它是按需提供的,但它不是线程安全的。
A *a_singleton = NULL;
A *getA() {
if (!a_singleton)
a_singleton = new A();
return a_singleton;
}
Run Code Online (Sandbox Code Playgroud)
call_once解决了这两个问题。当然,您可以使用其他同步原语的某种组合来代替,但最终只能重新实现您自己的call_once.
缓存和惰性评估。假设一个不可变类有一个存储成本低但计算成本高的属性,double foo() const;。您可以这样做,而不是按需计算或预先计算
private:
mutable std::once_flag m_flag;
mutable double m_foo;
double doCalcFoo() const; // Expensive!
public:
double foo() const {
std::call_once(m_flag, [this] { m_foo = doCalcFoo(); });
return m_foo;
}
Run Code Online (Sandbox Code Playgroud)
虽然你可以做
private:
mutable std::optional<double> m_foo;
mutable std::mutex m_fooMutex;
double doCalcFoo() const; // Expensive!
public:
double foo() const {
std::lock_guard lock{m_fooMutex};
if (!m_foo) {
m_foo = doCalcFoo();
}
return *m_foo;
}
Run Code Online (Sandbox Code Playgroud)
这是更多的字节(40 + 16 = 56 字节与 Clang 上的 4 + 8 + padding = 16 相比),性能较差,并且违反了 Parent 的“无原始同步基元”的更好代码目标:( https://sean -parent .stlab.cc/presentations/2016-08-08-concurrency/2016-08-08-concurrency.pdf幻灯片 6 至 11)。
想象一个带有一些巨大数据的单例实例(出于某种原因):
class Singleton {
public: static Singleton& get();
...
private: static std::unique_ptr<SingletonDataBase> instance;
}
Run Code Online (Sandbox Code Playgroud)
我们如何确保 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()
}
Run Code Online (Sandbox Code Playgroud)
每当您需要只调用一次时,使用call_once.
| 归档时间: |
|
| 查看次数: |
572 次 |
| 最近记录: |