c++ - std::call_once vs std::mutex 用于线程安全初始化

标签 c++ multithreading c++11

我对 std::call_once 的用途有点困惑。需要明确的是,我完全了解 std::call_once 的作用 以及如何使用它。它通常用于原子地初始化某个状态,并确保只有一个线程初始化该状态。我还在网上看到许多尝试使用 std::call_once 创建线程安全的单例。

作为 demonstrated here ,假设您编写了一个线程安全的单例,如下所示:

CSingleton& CSingleton::GetInstance()
{
    std::call_once(m_onceFlag, [] {
        m_instance.reset(new CSingleton);
    });
    return *m_instance.get();
}

好的,我明白了。但我认为 std::call_once 真正保证的唯一事情是传递的函数将 only 执行一次。但它是否保证如果多个线程之间存在调用函数的竞赛,并且一个线程获胜,其他线程将阻塞直到获胜的线程从打电话?

因为如果是这样,我认为 call_once 和普通同步互斥体之间没有区别,例如:

CSingleton& CSingleton::GetInstance()
{
    std::unique_lock<std::mutex> lock(m_mutex);
    if (!m_instance)
    {
      m_instance.reset(new CSingleton);
    }
    lock.unlock();

    return *m_instance;
}

那么,如果 std::call_once 确实强制其他线程阻塞,那么 std::call_once 相对于常规互斥体有什么好处?再想一想,std::call_once 肯定会 强制其他线程阻塞,或者在用户提供的函数中完成的任何计算都不会同步。再说一遍,std::call_once 在普通互斥体之上提供了什么?

最佳答案

call_once 为您做的一件事是处理异常。也就是说,如果第一个线程在仿函数内抛出异常(并将其传播出去),call_once 将不会认为 call_once 满足。允许随后的调用再次进入仿函数,以无异常(exception)地完成它。

在您的示例中,异常(exception)情况也得到了妥善处理。然而,很容易想象一个更复杂的仿函数,其中的异常(exception)情况不会得到正确处理。

说了这么多,我注意到 call_once 对于函数局部静态是多余的。例如:

CSingleton& CSingleton::GetInstance()
{
    static std::unique_ptr<CSingleton> m_instance(new CSingleton);
    return *m_instance;
}

或者更简单地说:

CSingleton& CSingleton::GetInstance()
{
    static CSingleton m_instance;
    return m_instance;
}

以上内容等同于您使用 call_once 的示例,恕我直言,更简单。哦,除了这个和你的例子之间的破坏顺序有很大的不同。在这两种情况下,m_instance 都会以相反的构造顺序被销毁。但构建顺序不同。在您的 m_instance 中,是相对于同一翻译单元中具有文件本地范围的其他对象构造的。使用 function-local-statics,在第一次执行 GetInstance 时构造 m_instance

这种差异对您的应用程序可能很重要,也可能不重要。一般来说,我更喜欢函数局部静态解决方案,因为它是“懒惰的”。 IE。如果应用程序从不调用 GetInstance(),则永远不会构造 m_instance。并且在应用程序启动期间没有一段时间试图一次构建大量静态。您只需在实际使用时支付 build 费用。

关于c++ - std::call_once vs std::mutex 用于线程安全初始化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26985370/

相关文章:

C++:需要从几个类似接口(interface)的类继承的参数类型

c++ - 如何禁止复制构造函数并仅使用移动构造函数?

C++ 井字游戏 AI

c++ - 在每个逗号分隔的对象上执行成员函数

c# - 多线程/C# : Can I do a BeginInvoke on multiple UI elements?

java - java中的循环任务

c++ - 为什么 shared_ptr 需要保存 weak_ptr 的引用计数?

c++ - 如何在 Visual Studio 2005 中创建 ATL/C++ ActiveX DLL

java - 带信号量的多线程

c++ - 通过引用 std::inner_product 传递迭代器