如何在不使用<mutex>的情况下在C++ 11中实现多线程安全单例

NoS*_*tAl 70 c++ singleton multithreading atomic c++11

既然C++ 11有多线程,我想知道在不使用互斥锁的情况下实现延迟初始化单例的正确方法是什么(出于性能原因).我想出了这个,但是我并不擅长编写无锁代码,所以我正在寻找更好的解决方案.

// ConsoleApplication1.cpp : Defines the entry point for the console application.
//
# include <atomic>
# include <thread>
# include <string>
# include <iostream>
using namespace std;
class Singleton
{

public:
    Singleton()
    {
    }
static  bool isInitialized()
    {
        return (flag==2);
    }
static  bool initizalize(const string& name_)
    {
        if (flag==2)
            return false;// already initialized
        if (flag==1)
            return false;//somebody else is initializing
        if (flag==0)
        {
            int exp=0;
            int desr=1;
            //bool atomic_compare_exchange_strong(std::atomic<T>* obj, T* exp, T desr)
            bool willInitialize=std::atomic_compare_exchange_strong(&flag, &exp, desr);
            if (! willInitialize)
            {
                //some other thread CASed before us
                std::cout<<"somebody else CASed at aprox same time"<< endl;
                return false;
            }
            else 
            {
                initialize_impl(name_);
                assert(flag==1);
                flag=2;
                return true;
            }
        }
    }
static void clear()
{
    name.clear();
    flag=0;
}
private:
static  void initialize_impl(const string& name_)
{
        name=name_;
}
static  atomic<int> flag;
static  string name;
};
atomic<int> Singleton::flag=0;
string Singleton::name;
void myThreadFunction()
{
    Singleton s;
    bool initializedByMe =s.initizalize("1701");
    if (initializedByMe)
        s.clear();

}
int main()
{
    while (true)
    {
        std::thread t1(myThreadFunction);
        std::thread t2(myThreadFunction);
        t1.join();
        t2.join();
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

请注意,这clear()仅用于测试,真正的单例不具备该功能.

Xeo*_*Xeo 142

C++ 11不再需要手动锁定.如果已经初始化静态局部变量,则并发执行应该等待.

§6.7 [stmt.dcl] p4

如果控制在初始化变量时同时进入声明,则并发执行应等待初始化完成.

因此,简单有这样的static功能:

static Singleton& get() {
  static Singleton instance;
  return instance;
}
Run Code Online (Sandbox Code Playgroud)

这将在C++ 11中正常工作(当然,只要编译器正确实现了标准的那部分).


当然,真正正确的答案是,以使用单,期.

  • +1,如果仅用于`当然,真正正确的答案是不使用单身,期间...... ... :-) ... (6认同)
  • `“当然,真正正确的答案是不要使用单例,句号。”` - 我不同意,单例是有目的的,它们被过度使用和误用了很多,但它们有一个目的。 (5认同)
  • 当然不能保证实现不使用mutices来确保行为,所以"没有性能原因的突变"可能不会被满足(Singleton初始化是正确的地方,担心这当然是值得商榷的) (4认同)
  • @Grizzly:嗯,它没有"使用`<mutex>`".;) (4认同)
  • @Leandros那会是哪一个? (2认同)
  • @Leandros好吧,不要让我们像那样挂在那里.给我们一个你的经验的例子. (2认同)

Gut*_*Mac 37

对我来说,使用C++ 11实现单例的最佳方法是:

class Singleton {
 public:
  static Singleton& Instance() {
    // Since it's a static variable, if the class has already been created,
    // it won't be created again.
    // And it **is** thread-safe in C++11.
    static Singleton myInstance;

    // Return a reference to our instance.
    return myInstance;
  }

  // delete copy and move constructors and assign operators
  Singleton(Singleton const&) = delete;             // Copy construct
  Singleton(Singleton&&) = delete;                  // Move construct
  Singleton& operator=(Singleton const&) = delete;  // Copy assign
  Singleton& operator=(Singleton &&) = delete;      // Move assign

  // Any other public methods.

 protected:
  Singleton() {
    // Constructor code goes here.
  }

  ~Singleton() {
    // Destructor code goes here.
  }

 // And any other protected methods.
}
Run Code Online (Sandbox Code Playgroud)

  • `static Singleton myInstance;`应该在实现中定义而不是标题. (3认同)

Whe*_*zil 5

恕我直言,实现单例的最佳方法是使用“双重检查,单锁”模式,您可以在 C++ 11 中便携地实现 该模式:在 C++11 中修复双重检查锁定这种模式在已经 -创建案例,只需要一个指针比较,并且在第一个用例中是安全的。

正如前面的回答中提到的,C++ 11 保证静态局部变量的构造顺序安全 C++11 中的局部静态变量初始化线程安全吗?所以你可以安全地使用这种模式。但是,Visual Studio 2013 尚不支持它:-(请参阅此页面上的“magic statics”行,因此如果您使用的是 VS2013,您仍然需要自己完成。

不幸的是,没有什么是简单的。不能从 CRT 初始化中调用上面模式引用的示例代码,因为静态 std::mutex 有一个构造函数,因此不能保证在第一次调用之前初始化以获取单例,如果所述调用是一个侧面 - CRT 初始化的影响。为了解决这个问题,你必须使用,而不是互斥锁,而是一个指向互斥锁的指针,它保证在 CRT 初始化开始之前被初始化为零。然后你将不得不使用 std::atomic::compare_exchange_strong 来创建和使用互斥锁。

我假设 C++ 11 线程安全本地静态初始化语义即使在 CRT 初始化期间调用也能工作。

因此,如果您有可用的 C++ 11 线程安全本地静态初始化语义,请使用它们。如果没有,您还有一些工作要做,如果您希望单例在 CRT 初始化期间是线程安全的,则更是如此。