高效的线程安全单例在C ++中
单身类的通常模式是类似的
static Foo &getInst() { static Foo *inst = NULL; if(inst == NULL) inst = new Foo(...); return *inst; }
然而,我的理解是这个解决scheme不是线程安全的,因为1)Foo的构造函数可能会被调用多次(这可能会也可能不重要),2)在返回到不同的线程之前,inst可能不会被完全构造。
一种解决方法是在整个方法中包装一个互斥锁,但是在实际需要之后,我花费了很长的时间同步开销。 另一种是类似的
static Foo &getInst() { static Foo *inst = NULL; if(inst == NULL) { pthread_mutex_lock(&mutex); if(inst == NULL) inst = new Foo(...); pthread_mutex_unlock(&mutex); } return *inst; }
这是正确的做法,还是我应该知道的任何陷阱? 例如,是否有任何可能发生的静态初始化顺序问题,即inst第一次调用getInst时,inst始终保证为NULL?
你的解决scheme被称为“双重检查locking”,你写的方式不是线程安全的。
这个Meyers / Alexandrescu的文件解释了为什么 – 但这篇文章也被广泛误解。 它启动了“双重检查locking在C ++中是不安全的” – 但是它的实际结论是,C ++中的双重检查locking可以安全地实现,只需要在非显而易见的地方使用内存屏障。
该文件包含伪代码,演示如何使用内存屏障来安全地实现DLCP,所以您不应该很难纠正您的实现。
如果你正在使用C ++ 11,这是一个正确的方法来做到这一点:
Foo& getInst() { static Foo inst(...); return inst; }
根据新的标准,没有必要再关心这个问题了。 对象初始化只能由一个线程完成,其他线程将一直等待完成。 或者你可以使用std :: call_once。 (更多信息在这里 )
使用pthread_once
,这是保证初始化函数运行一次primefaces。
(在Mac OS X上,它使用自旋锁,不知道其他平台的实现情况。)
Herb Sutter谈到CppCon 2014的双重locking。
下面是我在C ++ 11基础上实现的代码:
class Foo { public: static Foo* Instance(); private: Foo() {} static atomic<Foo*> pinstance; static mutex m_; }; atomic<Foo*> Foo::pinstance { nullptr }; std::mutex Foo::m_; Foo* Foo::Instance() { if(pinstance == nullptr) { lock_guard<mutex> lock(m_); if(pinstance == nullptr) { pinstance = new Foo(); } } return pinstance; }
您也可以在这里查看完整的程序: http : //ideone.com/olvK13
TTBOMK,唯一保证线程安全的方式来做到这一点,而不是locking将是在您启动线程之前初始化所有的单身人士。
您的替代品被称为“双重检查locking” 。
可能存在multithreading内存模型,但POSIX并不能保证它
ACE单例实现使用双重检查locking模式进行线程安全,如果您愿意,可以参考它。
你可以在这里find源代码。