OnTheWay2012
埋葬昨天的我,迎来重生的我!
posts - 15,  comments - 89,  trackbacks - 0
昨天写了一篇随笔,里面提到了单例模式,本来没有想多说单例模式,可是有人回复说让我先看看线程安全再来谈设计模式。
今天闲来无事就随便写了个线程安全的设计模式,具体代码如下:
  1#include <iostream>
  2#include <vector>
  3#include <bitset>
  4#include <assert.h>
  5#include <Windows.h>
  6#include <process.h>
  7
  8using namespace std;
  9
 10class CSingleton
 11{
 12private:
 13    class CAssistForSingleton
 14    {
 15    private:
 16        CRITICAL_SECTION m_cs;
 17
 18    public:
 19        CAssistForSingleton()
 20        {
 21            InitializeCriticalSection(&m_cs);
 22        }

 23
 24        ~CAssistForSingleton()
 25        {
 26            DeleteCriticalSection(&m_cs);
 27        }

 28
 29    public:
 30        void Lock()
 31        {
 32            EnterCriticalSection(&m_cs);
 33        }

 34
 35        void UnLock()
 36        {
 37            LeaveCriticalSection(&m_cs);
 38        }

 39    }
;
 40
 41private:
 42    static CAssistForSingleton m_refSycObj;
 43    static CSingleton *m_pInstance;
 44
 45    static int m_nData;
 46
 47private:
 48    CSingleton()
 49    {
 50
 51    }

 52
 53public:
 54    static CSingleton *GetInstatnce()
 55    {
 56        m_refSycObj.Lock();
 57        if (NULL == m_pInstance)
 58        {
 59            m_pInstance = new CSingleton;
 60            cout<<"new CSingleton"<<endl;
 61        }

 62        m_refSycObj.UnLock();
 63
 64        return m_pInstance;
 65    }

 66
 67public:
 68    static int GetData() 
 69    {
 70        return m_nData;
 71    }

 72
 73    static void SetData(int nData)
 74    {
 75        m_refSycObj.Lock();
 76        m_nData = nData;
 77        m_refSycObj.UnLock();
 78    }

 79}
;
 80
 81CSingleton::CAssistForSingleton CSingleton::m_refSycObj = CSingleton::CAssistForSingleton();
 82CSingleton *CSingleton::m_pInstance = NULL;
 83int CSingleton::m_nData = 0;
 84
 85unsigned int WINAPI ThreadFun(void *)
 86{
 87    cout<<"Launcher Thread"<<endl;
 88    
 89    for(int i = 0 ; i < 99999999 ; i++)
 90    {
 91        CSingleton *pSingl = CSingleton::GetInstatnce();
 92        if (NULL != pSingl)
 93        {
 94            pSingl->SetData(i);
 95        }

 96
 97        Sleep(500);
 98    }

 99
100    return 0;
101}

102
103int main(int argv, char *argc[])
104{
105    uintptr_t HandleThread[10];
106    unsigned int nThreadId = 0;
107    for(int i = 0 ; i < 10 ; i++)
108    {
109        HandleThread[i] = _beginthreadex(NULL, 0, ThreadFun, NULL, 0&nThreadId);
110    }

111
112    WaitForMultipleObjects(10, (const HANDLE *)HandleThread, TRUE, INFINITE);
113
114    return 0;
115}

116
117

以上就是我的实现,有什么问题欢迎批评指针。
posted on 2010-05-21 09:52 OnTheWay 阅读(5538) 评论(15)  编辑 收藏 引用 所属分类: 软件设计

FeedBack:
# re: 一种线程安全的单例模式实现方式
2010-05-21 10:15 | 战魂小筑
1. 可以不用单件, 程序开始时全局new出来, 以后不用锁,直接用就好.
2. 既然全是静态数据, 还需要用Singleton呢?
3. 用Interlock会更高效  回复  更多评论
  
# re: 一种线程安全的单例模式实现方式
2010-05-21 10:52 | GunsNRose
之前看过一个,建议将构造弄成 私有的,生成实例由 GetInstatnce来做
别人new是new不了的  回复  更多评论
  
# re: 一种线程安全的单例模式实现方式
2010-05-21 15:37 | OwnWaterloo
依然不是线程安全的。  回复  更多评论
  
# re: 一种线程安全的单例模式实现方式
2010-05-21 21:02 | ccsdu2009
去看看ZThread中的单件吧

以前的话我会推荐loki的
但是现在归纳绝那个太复杂了  回复  更多评论
  
# re: 一种线程安全的单例模式实现方式[未登录]
2010-05-22 07:17 | OnTheWay
@OwnWaterloo
您好,首先感谢您对本随笔的关注。恳请您说详细一点,我不太明白哪些地方还不是线程安全的。  回复  更多评论
  
# re: 一种线程安全的单例模式实现方式
2010-05-22 08:57 | OwnWaterloo
@OnTheWay
class S
{
S() { ... }
~S(); { ... }
S(S const&);
S& operator=(S const&S);

public:
static S& instance()
{
static S s;
return s;
}

};

你认为C++(C++03)是否保证S::instance是线程安全的?
如果是, 请说明理由。
如果C++不保证, 是否应该将S::instance作成线程安全的?
  回复  更多评论
  
# re: 一种线程安全的单例模式实现方式
2010-05-22 09:00 | OwnWaterloo
@OnTheWay
再来一种:

class S
{
// ctor, copy, dtor, assignment
static S s_;
public:
S& instance() { return s_; }
};

S S::s_;

同样是上面的问题。
  回复  更多评论
  
# re: 一种线程安全的单例模式实现方式[未登录]
2010-05-22 09:12 | OnTheWay
@OwnWaterloo
我觉得不是线程安全的,原因如下:
在你给出的代码中类S中的函数(虽然现在我还没有想不出这种函数)可能不是线程安全的。

我的MSN是wwj_5_209@163.com,能否加我一下,我们讨论一下。  回复  更多评论
  
# re: 一种线程安全的单例模式实现方式
2010-05-22 14:59 | 匿名
@OwnWaterloo
与博文中的不是一回事。  回复  更多评论
  
# re: 一种线程安全的单例模式实现方式
2010-05-22 19:08 | OwnWaterloo
@OnTheWay
注意两种需要运行时初始化的静态对象, s和s_。
前者的初始化时机C++有保证, 但不保证多线程安全。
后者的初始化时机C++只保证同一翻译单元内中有顺序。

再看static CAssistForSingleton m_refSycObj;
这就属于第2种。

下面的情况有发生的可能性:
1. 另一翻译单元的静态对象先于m_refSycObj被初始化
2. 在它初始化时访问了CSingleton *GetInstatnce()

此时就访问了一个"未初始化"的临界区。
这已经是bug。

再有, 如果有下列情况:
1. 一些先于m_refSycObj初始化的代码开启了线程
2. 多个线程在m_refSycObj初始化前访问CSingleton *GetInstatnce()

一个未初始化的m_refSycObj根本不能用于同步。

所以, 上面的第1个问题: 这样做不是多线程安全的。


而静态对象在构造时启动线程的情况并不多, 所以并不一定需要将s_作成多线程安全。
例如boost就是这样, 要求在进入main之前, 是不许有两条以上的执行路径去访问。
这就是第2个问题。


如果需要完全的多线程安全:
1. 使用once_initial函数(pthread或者win6有提供)
2. 使用"可以静态初始化"的锁, 比如pthread_mutex_t就可以
或者自己使用一个spinlock也行。

我没有msn…… 用gmail的同名邮箱。
  回复  更多评论
  
# re: 一种线程安全的单例模式实现方式
2010-05-22 19:09 | OwnWaterloo
@匿名
知道什么叫"举一反三"吗?
  回复  更多评论
  
# re: 一种线程安全的单例模式实现方式[未登录]
2010-05-22 22:50 | ~
个人一点随想:我觉得应该从实际应用的逻辑上来进行线程安全的分析,并根据现实情况的分析避免不必要的加解锁操作。某些代码只是一种理想化的东西。现实中有太多比SetData复杂的逻辑。而且也不一定需要加锁。当然从你这个代码看,GetInstatnce当然是正确的哈。  回复  更多评论
  
# re: 一种线程安全的单例模式实现方式
2010-05-27 00:02 | GunsNRose
傻逼实现也好意思贴出来?丢人现眼  回复  更多评论
  
# re: 一种线程安全的单例模式实现方式[未登录]
2010-05-27 08:56 | cppexplore
@OnTheWay
c++里的线程安全单例应该是典型的双重检测.
文中如此实现的话, 每次调用GetInstatnce都要涉及到加锁操作, 估计没人愿意调用.   回复  更多评论
  
# re: 一种线程安全的单例模式实现方式
2010-10-30 14:27 | 杨云召
没有用volatile,显然不是线程安全的  回复  更多评论
  

只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理



<2010年5月>
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345

常用链接

留言簿(4)

随笔分类

随笔档案

友情连接

搜索

  •  

最新评论

阅读排行榜

评论排行榜