博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
c++之单例模式
阅读量:6816 次
发布时间:2019-06-26

本文共 5008 字,大约阅读时间需要 16 分钟。

1 本篇主要讨论下多线程下的单例模式实现:

  首先是 double check 实现方式: 这种模式可以满足多线程环境下,只产生一个实例。

template
class dclsingleton { public: static T& GetInstance() { if(NULL == value_) { MutexGuard mg(mutex_); if (NULL == value_) { value_ = new T; } } return *value_; } protected: dclsingleton() { std::cout << "dclsingleton constuctor called" << std::endl; } dclsingleton(const dclsingleton &dcl) {} private: static T* value_; static Mutex mutex_; };

   但是这种实现存在除bug的隐患, 问题就在: value_ = new T; 上。《程序员的自我修养》上指出:

  这样的代码是有问题的,问题的来源在于 cpu 的乱序执行。c++里的new 包含了两步。

  (1)分配内存

  (2)调用构造函数

  所以 value_ = new T;  实际上包含了三步:

  (1)分配内存

  (2)在分配内存的位置上调用构造函数  

  (3)将内存地址赋值给 value_;

  这三步中,(2), (3)两步是可以颠倒的,也就是说,可能出现,先执行(3)这是 value_已经不为NULL, 当出现另一个对GetInstance的并发调用,if 内的 value_ != NULL于是返回,但是还没有调用构造函数。于是使用这个指针的时候,就会导致崩溃。

  这时候需要保证(2), (3)的执行顺序,通常需要加上内存屏障,保证一定保证(2)执行完以后,再执行(3)

  这里我加上了__sync_synchronize(); 后 实现是这样的:

  

       static T& GetInstance()            {                if(NULL == value_)                {                    MutexGuard mg(mutex_);                    if (NULL == value_)                    {                        T* tmp = static_cast
(operator new (sizeof(T))); new (tmp) T(); __sync_synchronize(); value_ = tmp; } } return *value_; }

 

    这样便可以既保证多线程环境安全,又保证不会出现上面的问题。

2. 加上内存屏障的示例代码:dcl_single.h

  

#ifndef __DCL_SINGLE_H#define __DCL_SINGLE_H#include 
namespace yl{ class Mutex { public: Mutex() { pthread_mutex_init(&mutex_, NULL); } ~Mutex() { pthread_mutex_destroy(&mutex_); } public: void Lock() { pthread_mutex_lock(&mutex_); } void UnLock() { pthread_mutex_unlock(&mutex_); } private: pthread_mutex_t mutex_; }; class MutexGuard { public: MutexGuard(Mutex& m) : mutex_(m) { mutex_.Lock(); } ~MutexGuard() { mutex_.UnLock(); } private: Mutex mutex_; }; template
class dclsingleton { public: static T& GetInstance() { if(NULL == value_) { MutexGuard mg(mutex_); if (NULL == value_) { T* tmp = static_cast
(operator new (sizeof(T))); new (tmp) T(); __sync_synchronize(); value_ = tmp; } } return *value_; } protected: dclsingleton() { std::cout << "dclsingleton constuctor called" << std::endl; } dclsingleton(const dclsingleton &dcl) {} private: static T* value_; static Mutex mutex_; }; template
T* dclsingleton
::value_ = NULL; template
Mutex dclsingleton
::mutex_;}#endif
View Code

 

  singletonTest.cpp

#include "dcl_single.h"#include 
namespace yl{ class MgrSg : public dclsingleton
{ private: friend class dclsingleton
; MgrSg(){ std::cout << "MgrSg: constructor called" << std::endl; } ~MgrSg() { std::cout << "MgrSg: desconstructor called" << std::endl; } public: void print() { std::cout << "print called" << std::endl; } };}intmain(void){ using namespace yl; MgrSg::GetInstance().print(); return 0;}

 

3. 还可以用 unix 下的 pthread_once 来实现单例模式:

  

template 
class MySingleton { public: static T & getInstance() { pthread_once(&ponce_, &MySingleton::init); return *instance; } protected: MySingleton() {} MySingleton(const MySingleton&) {} private: static void init() { instance = new T(); } private: static pthread_once_t ponce_; static T *instance; }; template
pthread_once_t MySingleton
::ponce_ = PTHREAD_ONCE_INIT; template
T *MySingleton
::instance = nullptr;}

 

4.我自己遇到的就是以上两种情况,若是希望了解更全面,可参考下边:

  http://www.cnblogs.com/liyuan989/p/4264889.html

5. 水平有限,望及时指出错误。谢谢

  

转载于:https://www.cnblogs.com/newbeeyu/p/6885098.html

你可能感兴趣的文章
将一个数的二进制位模式从左到右翻转并输出
查看>>
关于JEPLUS软件介绍——JEPLUS软件快速开发平台
查看>>
《编写可读代码的艺术》读书文摘--第一部分 表面层次的改进
查看>>
使用Nodejs创建基本的网站 Microblog--《Node.js开发指南》 3
查看>>
网管工作是否值得做下去?
查看>>
神行者PD10-adb push逃脱ro权限
查看>>
JPA(四)之实体关系一对一
查看>>
如何使用羊驼自动生成缩略图的功能。
查看>>
定制化Azure站点Java运行环境(1)
查看>>
inotify用法简介及结合rsync实现主机间的文件实时同步
查看>>
php 判断手机登陆
查看>>
git 问题
查看>>
Fedora18设置终端快捷键 和 桌面快捷方式
查看>>
取消NavigationBar左右两边的空隙
查看>>
MEMCACHE常用的命令
查看>>
Android 不显示光标或者光标颜色为白色的解决方法
查看>>
C#网络编程之---TCP协议的同步通信(二)
查看>>
thinkphp-许愿墙-3
查看>>
linux awk时间计算脚本
查看>>
杭电3635--Dragon Balls(并查集)
查看>>