想必每一位程序员都对设计模式中的单例模式非常的熟悉吧,以往我们用C++实现一个单例模式需要写以下代码:

 class CSingleton
{
private:
CSingleton() //构造函数是私有的
{
}
static CSingleton *m_pInstance;
public:
static CSingleton * GetInstance()
{
if (m_pInstance == NULL) //判断是否第一次调用
m_pInstance = new CSingleton();
return m_pInstance;
}
};

当然,这份代码在单线程环境下是正确无误的,但是当拿到多线程环境下时这份代码就会出现race condition,因此为了能在多线程环境下实现单例模式,我们首先想到的是利用同步机制来正确的保护我们的shared data,于是在多线程环境下单例模式代码就变成了下面这样:

 class CSingleton
{
private:
CSingleton() //构造函数是私有的
{
}
static CSingleton *m_pInstance;
mutex mtx;
public:
static CSingleton * GetInstance()
{
mtx.lock();
if (m_pInstance == NULL) //判断是否第一次调用
m_pInstance = new CSingleton();
mtx.unlock();
return m_pInstance;
}
};

正确是正确了,问题是每次调用GetInstance函数都要进入临界区,尤其是在heavy contention情况下函数将会成为系统的性能瓶颈,我们伟大的程序员发现我们不必每次调用GetInstance函数时都去获取锁,只是在第一次new这个实例的时候才需要同步,所以伟大的程序员们发明了著名的DCL技法,即Double Check Lock,代码如下:

 Widget* Widget::pInstance{ nullptr };
Widget* Widget::Instance() {
if (pInstance == nullptr) { // 1: first check
lock_guard<mutex> lock{ mutW };
if (pInstance == nullptr) { // 2: second check
pInstance = new Widget();
}
}
return pInstance;
}

曾今有一段时间,这段代码是被认为正确无误的,但是一群伟大的程序员们发现了其中的bug!并且联名上书表示这份代码是错误的。要解释其中为什么出现了错误,需要读者十分的熟悉memory model,这里我就不详细的说明了,一句话就是在这份代码中第三行代码:if (pInstance == nullptr)和第六行代码pInstance = new Widget();没有正确的同步,在某种情况下会出现new返回了地址赋值给pInstance变量而Widget此时还没有构造完全,当另一个线程随后运行到第三行时将不会进入if从而返回了不完全的实例对象给用户使用,造成了严重的错误。在C++11没有出来的时候,只能靠插入两个memory barrier来解决这个错误,但是C++11已经出现了好几年了,其中我认为最重要的是引进了memory model,从此C++11也能识别线程这个概念了!

  因此,在有了C++11后我们就可以正确的跨平台的实现DCL模式了,代码如下:

 atomic<Widget*> Widget::pInstance{ nullptr };
Widget* Widget::Instance() {
if (pInstance == nullptr) {
lock_guard<mutex> lock{ mutW };
if (pInstance == nullptr) {
pInstance = new Widget();
}
}
return pInstance;
}

C++11中的atomic类的默认memory_order_seq_cst保证了3、6行代码的正确同步,由于上面的atomic需要一些性能上的损失,因此我们可以写一个优化的版本:

 atomic<Widget*> Widget::pInstance{ nullptr };
Widget* Widget::Instance() {
Widget* p = pInstance;
if (p == nullptr) {
lock_guard<mutex> lock{ mutW };
if ((p = pInstance) == nullptr) {
pInstance = p = new Widget();
}
}
return p;
}

但是,C++委员会考虑到单例模式的广泛应用,所以提供了一个更加方便的组件来完成相同的功能:

 static unique_ptr<widget> widget::instance;
static std::once_flag widget::create;
widget& widget::get_instance() {
std::call_once(create, [=]{ instance = make_unique<widget>(); });
return instance;
}

可以看出上面的代码相比较之前的示例代码来说已经相当的简洁了,但是!!!有是但是!!!!在C++memory model中对static local variable,说道:The initialization of such a variable is defined to occur the first time control passes through its declaration; for multiple threads calling the function, this means there’s the potential for a race condition to define first.因此,我们将会得到一份最简洁也是效率最高的单例模式的C++11实现:

 widget& widget::get_instance() {
static widget instance;
return instance;
}

用Herb Sutter的话来说这份代码实现是“Best of All”的。

最新文章

  1. 一起学HTML基础-JavaScritp简介与语法
  2. Javascript事件冒泡机制
  3. linux概念之时间与时区
  4. Anchor和Dock的区别
  5. 《Python 学习手册4th》 第十六章 函数基础
  6. 比较两个序列字典序(lexicographicallySmaller)
  7. git的使用方法总结
  8. &gt;/dev/null 2&gt;&amp;1 这句话的含义
  9. Lintcode--011(打劫房屋2)
  10. windows后台服务程序编写
  11. (萌O(∩_∩)O)哈希知识点小结
  12. Cocos2d-x Auto-batching 浅浅的”深入分析”
  13. HDFS存储系统
  14. 《微信小程序七日谈》- 第六天:小程序devtool隐藏的秘密
  15. 【VB超简单入门】六、基本数据类型
  16. nodejs 后台服务启动
  17. 14. leetcode 383. Ransom Note
  18. objc一个NetConnector类示例
  19. count()聚合函数正确用法
  20. 清除DNS缓存(解决能上QQ但是无法上网页问题)

热门文章

  1. BZOJ3218 UOJ#77 A+B Problem(最小割+主席树)
  2. Debian 7 安装 wireshark
  3. Android AbsListView 的item动画类库 —— JazzyListView
  4. Restore Oracle database to another server
  5. maven pox配置
  6. hdu 5661 Claris and XOR
  7. c语言到汇编的学习
  8. oracle ORA-12514: TNS: no listener 解决方案
  9. UIAlertController 使用
  10. C++混合编程之idlcpp教程Python篇(9)