您的位置:首页 > 编程语言 > C语言/C++

C++单例模式与线程安全

2013-11-07 21:21 351 查看

C++单例模式

单例模式的主要作用是去除全局变量,这个意义在设计模式或者相关的资料中都有比较详细的介绍。

一、 单例常见实现方式

单例的实现方式比较常见的可以分为两个大类:类静态成员指针,函数静态变量。其中《设计模式》书中主要以类静态成员指针进行了讲解,而在《More effective c++》书中介绍了一种更高效的基于函数静态变量的实现方式,下面简单的对它们进行一下显示和总结。

1.1 类静态成员指针实现示例

为了便于展示,将所有的实现都放到了头文件中,代码如下:

1. 懒惰初始化实现

class CMemberSingleton

{

public:

~CMemberSingleton(void){}

staticCMemberSingleton* GetInstance();

private:

CMemberSingleton(void){}

staticCMemberSingleton *m_pInstance;

};

CMemberSingleton*CMemberSingleton::m_pInstance = NULL;

CMemberSingleton* CMemberSingleton::GetInstance()

{

if(NULL != m_pInstance)

{

m_pInstance= new CMemberSingleton;

}

returnm_pInstance;

}

2. 普通实现

class CMemberSingleton

{

public:

~CMemberSingleton(void){}

staticCMemberSingleton* GetInstance();

private:

CMemberSingleton(void){}

staticCMemberSingleton *m_pInstance;

};

CMemberSingleton*CMemberSingleton::m_pInstance = new CMemberSingleton;

CMemberSingleton*CMemberSingleton::GetInstance()

{

returnm_pInstance;

}

3. 总结

基于静态成员变量的单例实现,无法实现对于单例的销毁工作,当然直接提供一个DestoryInstance这种函数给使用者调用是可以,但是调用的时机却是很不好把握的。

1.2 函数静态变量实现示例

基于函数静态变量来实现单例可以解决1.1中基于类静态成员变量中实例销毁问题。示例代码如下:

class CMemberSingleton

{

friendCMemberSingleton& GetInstance();

public:

~CMemberSingleton(void){}

private:

CMemberSingleton(void){}

};

CMemberSingleton& GetInstance()

{

staticCMemberSingleton instance;

returninstance;

}

当然根据个人喜好,你也可以将GetInstance写成类的静态函数,实现效果是一致的。

1.3 自动销毁的基于类静态成员单例实现

如果说你确实非常喜欢基于静态成员来实现单例,或者某些因素强制你必须使用静态成员,我们也还是可能借助下面的方法来实现自动销毁工作。

class CMemberSingleton

{

public:

~CMemberSingleton(void){}

staticCMemberSingleton* GetInstance();

private:

CMemberSingleton(void){}

staticCMemberSingleton *m_pInstance;

classCGarbo

{

public:

~CGarbo()

{

if(NULL != CMemberSingleton::m_pInstance)

{

deleteCMemberSingleton::m_pInstance;

}

}

};

staticCGarbo m_garbo;

};

CMemberSingleton::CGarboCMemberSingleton::m_garbo = CMemberSingleton::CGarbo();

CMemberSingleton*CMemberSingleton::m_pInstance = NULL;

CMemberSingleton*CMemberSingleton::GetInstance()

{

returnm_pInstance;

}

二、 单例线程安全性

关于单例的实现方式上面已经进行了简单的介绍,如果我们不编写多线程代码,这一段的讨论你可以忽略。

单例是线程安全的吗(注意我们只讨论单例的获取,也就是常见的GetInstance,至于单例对应类的成员函数线程安全性,此处不作讨论)?回答是否定的!单例不是线程安全的,如果你实现的单例被多个线程调用,那么你就要注意了,很可能在某个时间出现异常(通常是程序启动的时候了!)。下面用一个例子展示一下,示例代码如下:

1. 单例代码

classCMemberSingleton

{

public:

~CMemberSingleton(void);

static CMemberSingleton* GetInstance();

private:

CMemberSingleton(void);

static CMemberSingleton *m_pInstance;

};

CMemberSingleton*CMemberSingleton::m_pInstance = NULL;

CMemberSingleton::CMemberSingleton(void)

{

cout<<"construct CMemberSingleton"<<endl;

}

CMemberSingleton::~CMemberSingleton(void)

{

cout<<"destoryCMemberSingleton"<<endl;

}

CMemberSingleton*CMemberSingleton::GetInstance()

{

if (NULL == m_pInstance)

{

m_pInstance = newCMemberSingleton;

}

return m_pInstance;

}

2. 主函数代码

void* thread_func(void *param)

{

CMemberSingleton::GetInstance();

return NULL;

}

int main(char **argv, int argc)

{

int i = 0;

pthread_t pids[100] ={0};

for (i = 0; i < 3;++i)

{

pthread_create(&pids[i],NULL, thread_func, NULL);

}

for (i = 0; NULL !=pids[i].p; ++i)

{

void *p = NULL;

pthread_join(pids[i],&p);

}

system("pause");

return 0;

}

3. 某次的执行结果

图1


执行结果可以明显看出,我们需要的是单例,却实际构造了三次,所以单例不一定是线程安全的。

4. 最简单的解决办法

关于解决单例线程安全的办法也许有很多,但是很多情况你都不可避免的要通过线程同步的方式来实现,下面介绍一种不需要线程同步的安全的单例实现。代码如下:

class CMemberSingleton

{

public:

~CMemberSingleton(void);

static CMemberSingleton*GetInstance();

private:

CMemberSingleton(void);

static CMemberSingleton*m_pInstance;

};

CMemberSingleton* CMemberSingleton::m_pInstance = newCMemberSingleton;

CMemberSingleton::CMemberSingleton(void)

{

cout<<"constructCMemberSingleton"<<endl;

}

CMemberSingleton::~CMemberSingleton(void)

{

cout<<"destoryCMemberSingleton"<<endl;

}

CMemberSingleton* CMemberSingleton::GetInstance()

{

return m_pInstance;

}

关于运行的结果就不再上图了,原理大家也应该明白,类的静态成员会在main函数运行之间就进行初始化的操作,如果我们不把它初始化为NULL,而是直接初始化一个对象给它,那么在有线程访问时,它已经初始化完成了,所以不会有线程同步的问题了。

虽然这个办法很好的解决了多线程安全问题,但是关于单例对象的销毁工作,就需要额外的实现了,你喜欢提供DestoryInstance这种函数,还是采用1.3中的“垃圾工”来帮你完成销毁任务呢?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: