通过例子学设计模式之--单例模式以及多线程下说明(C++实现)
2017-10-18 14:33
911 查看
单例模式应该是最简单的模式吧。比方说人,手可以都2只,但是大脑至于一个吧。
实现单例模式有不同的方式,本文主要说明不同的方式在多线程环境下的表现。
首先,使用最常用的方式,私有构造函数我特定加了一个睡眠1ms,然后创建多个线程同时获取单例,看看会发生什么。代码如下:
测试结果如下,可以看到我们已经拥有了2个不同的大脑啦。
如果把构造函数里面修改为 Sleep(10),那么会出现更多的大脑:
很明显啦,这种最简单的方式,由于实际项目中构造函数运行的时间可能会达到100ms以上,那么多线程同时获取实例的时候就很有可能会出现多个实例啦。怎么办?加锁吧。
这里我们使用C++的临界区。加入临界区的代码如下:
心想这下应该OK了吧,但是我们失望了,测试结果显示仍然出现了2个大脑(如果构造函数睡眠10ms,情况会更糟):
为什么呢?经过一番分析,原因如下:
//进入临界区
EnterCriticalSection(&g_Section); //n个线程执行这里,1个线程A继续往下,n-1个线程被卡住
m_obj = new Brain; //线程A创建对象,
LeaveCriticalSection(&g_Section);//线程A离开后,其它n-1个线程也会执行语句 m_obj = new Brain; 好了,出现了n个实例。
改进方法就是在创建之前我们再次判断单例是否为NULL,如下:
//进入临界区
EnterCriticalSection(&g_Section);
if(m_obj == NULL)//再次判断
{
m_obj = new Brain;
}
LeaveCriticalSection(&g_Section);
测试运行发现这下终于OK了。不管构造函数里面Sleep多久的时间,都只会有一个实例。
实现单例模式有不同的方式,本文主要说明不同的方式在多线程环境下的表现。
首先,使用最常用的方式,私有构造函数我特定加了一个睡眠1ms,然后创建多个线程同时获取单例,看看会发生什么。代码如下:
class Brain { public: static Brain* GetBrain() { if(m_obj == NULL) { m_obj = new Brain; } return m_obj; } static void Destory() { if(m_obj) { delete m_obj; m_obj = NULL; } } private: Brain() { Sleep(1);//先思考1ms } static Brain* m_obj; }; Brain* Brain::m_obj = NULL; unsigned int WINAPI GetInstanceTest(IN void* pContext) { Brain *s = Brain::GetBrain(); printf("s_Addr = 0x%x\r\n",s); return 0; } void CreateThreadTest(IN void *pContext) { HANDLE hThread = (HANDLE)_beginthreadex( NULL, 0, GetInstanceTest,(void*)pContext,0,NULL); if( hThread != INVALID_HANDLE_VALUE ) { CloseHandle(hThread);//防止句柄泄漏 } } void TestSingle() { for(int i=0;i<10;i++) { CreateThreadTest(NULL); } Brain::Destory(); }
测试结果如下,可以看到我们已经拥有了2个不同的大脑啦。
如果把构造函数里面修改为 Sleep(10),那么会出现更多的大脑:
很明显啦,这种最简单的方式,由于实际项目中构造函数运行的时间可能会达到100ms以上,那么多线程同时获取实例的时候就很有可能会出现多个实例啦。怎么办?加锁吧。
这里我们使用C++的临界区。加入临界区的代码如下:
extern CRITICAL_SECTION g_Section; class Brain { public: static Brain* GetBrain() { if(m_obj == NULL) { //进入临界区 EnterCriticalSection(&g_Section); m_obj = new Brain; LeaveCriticalSection(&g_Section); } return m_obj; } static void Destory() { if(m_obj) { delete m_obj; m_obj = NULL; } } private: Brain() { Sleep(1);//先思考1ms } static Brain* m_obj; }; Brain* Brain::m_obj = NULL; CRITICAL_SECTION g_Section = {0}; //加上临界区 unsigned int WINAPI GetInstanceTest(IN void* pContext) { Brain *s = Brain::GetBrain(); printf("s_Addr = 0x%x\r\n",s); return 0; } void CreateThreadTest(IN void *pContext) { HANDLE hThread = (HANDLE)_beginthreadex( NULL, 0, GetInstanceTest,(void*)pContext,0,NULL); if( hThread != INVALID_HANDLE_VALUE ) { CloseHandle(hThread);//防止句柄泄漏 } } void TestSingle() { InitializeCriticalSection(&g_Section);//初始化临界区 for(int i=0;i<10;i++) { CreateThreadTest(NULL); } Sleep(200);//睡眠200ms确保所有线程运行完成 Brain::Destory(); DeleteCriticalSection(&g_Section); }
心想这下应该OK了吧,但是我们失望了,测试结果显示仍然出现了2个大脑(如果构造函数睡眠10ms,情况会更糟):
为什么呢?经过一番分析,原因如下:
//进入临界区
EnterCriticalSection(&g_Section); //n个线程执行这里,1个线程A继续往下,n-1个线程被卡住
m_obj = new Brain; //线程A创建对象,
LeaveCriticalSection(&g_Section);//线程A离开后,其它n-1个线程也会执行语句 m_obj = new Brain; 好了,出现了n个实例。
改进方法就是在创建之前我们再次判断单例是否为NULL,如下:
//进入临界区
EnterCriticalSection(&g_Section);
if(m_obj == NULL)//再次判断
{
m_obj = new Brain;
}
LeaveCriticalSection(&g_Section);
测试运行发现这下终于OK了。不管构造函数里面Sleep多久的时间,都只会有一个实例。
相关文章推荐
- 通过例子学设计模式之--工厂方法模式以及使用场景说明(C++实现)
- 通过例子学设计模式之--简单工厂模式以及使用场景说明(C++实现)
- 通过例子学设计模式之--原型模式以及使用场景说明(C++实现)
- 通过例子学设计模式之--抽象工厂模式以及使用场景说明(C++实现)
- 通过例子学设计模式之--组合模式以及使用场景说明(C++实现)
- 通过例子学设计模式之--建造者模式以及使用场景说明(C++实现)
- 通过例子学设计模式之--桥接模式以及使用场景说明(C++实现)
- 通过例子学设计模式之--适配器模式以及使用场景说明(C++实现)
- 通过例子学设计模式之--外观模式以及使用场景说明(C++实现)
- 用例子说明MVC 设计模式(以Objective-C 实现)
- 《Head First 设计模式》例子的C++实现(1 策略模式)
- 设计模式之原型模式 c++实现以及详解
- Java设计模式(二):单例模式的5种实现方式,以及在多线程环境下5种创建单例模式的效率
- 《Head First 设计模式》例子的C++实现(2 观察者模式)
- 用例子说明MVC 设计模式(以Objective-C 实现)
- [设计模式]_[中级]_[模板方法C++例子实现举例]
- 用例子说明MVC 设计模式(以Objective-C 实现)
- [设计模式]_[中级]_[模板方法C++例子实现举例]
- 用例子说明MVC 设计模式(以Objective-C 实现)
- 《Head First 设计模式》例子的C++实现(1 策略模式)