C++程序员们,快来写最简洁的单例模式吧
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了C++程序员们,快来写最简洁的单例模式吧,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含3038字,纯文字阅读大概需要5分钟。
内容图文
![C++程序员们,快来写最简洁的单例模式吧](/upload/InfoBanner/zyjiaocheng/1323/7f79678c095749d69bd35648692f5897.jpg)
想必每一位程序员都对设计模式中的单例模式非常的熟悉吧,以往我们用C++实现一个单例模式需要写以下代码:
1 class CSingleton 2 { 3 private : 4 CSingleton() //构造函数是私有的 5 { 6 } 7static CSingleton *m_pInstance; 8public: 9static CSingleton * GetInstance() 10 { 11if (m_pInstance == NULL) //判断是否第一次调用12 m_pInstance = new CSingleton(); 13return m_pInstance; 14 } 15 };
当然,这份代码在单线程环境下是正确无误的,但是当拿到多线程环境下时这份代码就会出现race condition,因此为了能在多线程环境下实现单例模式,我们首先想到的是利用同步机制来正确的保护我们的shared data,于是在多线程环境下单例模式代码就变成了下面这样:
1 class CSingleton 2 { 3 private : 4 CSingleton() //构造函数是私有的 5 { 6 } 7static CSingleton *m_pInstance; 8 mutex mtx; 9public: 10static CSingleton * GetInstance() 11 { 12 mtx.lock(); 13if (m_pInstance == NULL) //判断是否第一次调用14 m_pInstance = new CSingleton(); 15 mtx.unlock(); 16return m_pInstance; 17 } 18 };
正确是正确了,问题是每次调用GetInstance函数都要进入临界区,尤其是在heavy contention情况下函数将会成为系统的性能瓶颈,我们伟大的程序员发现我们不必每次调用GetInstance函数时都去获取锁,只是在第一次new这个实例的时候才需要同步,所以伟大的程序员们发明了著名的DCL技法,即Double Check Lock,代码如下:
1 Widget* Widget::pInstance{ nullptr }; 2 Widget* Widget::Instance() { 3if (pInstance == nullptr) { // 1: first check 4 lock_guard<mutex> lock{ mutW }; 5if (pInstance == nullptr) { // 2: second check 6 pInstance = new Widget(); 7 } 8 } 9return pInstance; 10 }
曾今有一段时间,这段代码是被认为正确无误的,但是一群伟大的程序员们发现了其中的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模式了,代码如下:
1 atomic<Widget*> Widget::pInstance{ nullptr }; 2 Widget* Widget::Instance() { 3if (pInstance == nullptr) { 4 lock_guard<mutex> lock{ mutW }; 5if (pInstance == nullptr) { 6 pInstance = new Widget(); 7 } 8 } 9return pInstance; 10 }
C++11中的atomic类的默认memory_order_seq_cst保证了3、6行代码的正确同步,由于上面的atomic需要一些性能上的损失,因此我们可以写一个优化的版本:
1 atomic<Widget*> Widget::pInstance{ nullptr }; 2 Widget* Widget::Instance() { 3 Widget* p = pInstance; 4if (p == nullptr) { 5 lock_guard<mutex> lock{ mutW }; 6if ((p = pInstance) == nullptr) { 7 pInstance = p = new Widget(); 8 } 9 } 10return p; 11 }
但是,C++委员会考虑到单例模式的广泛应用,所以提供了一个更加方便的组件来完成相同的功能:
1 static unique_ptr<widget> widget::instance; 2static std::once_flag widget::create; 3 widget& widget::get_instance() { 4 std::call_once(create, [=]{ instance = make_unique<widget>(); }); 5return instance; 6 }
可以看出上面的代码相比较之前的示例代码来说已经相当的简洁了,但是!!!有是但是!!!!在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实现:
1 widget& widget::get_instance() { 2static widget instance; 3return instance; 4 }
用Herb Sutter的话来说这份代码实现是“Best of All”的。
原文:http://www.cnblogs.com/zxh1210603696/p/4157294.html
内容总结
以上是互联网集市为您收集整理的C++程序员们,快来写最简洁的单例模式吧全部内容,希望文章能够帮你解决C++程序员们,快来写最简洁的单例模式吧所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。