C++中多线程Singleton的实现

时间:2022-09-09 08:44:48


读了原作者的几篇博客,感觉对本人非常受用,于是转载到我的博客,方便之后查阅。在此表示对原作者的感谢。

原文链接:http://blog.chinaunix.net/uid-26611383-id-4281275.html


我想关于Singleton模式的实现和资料很多很多,这里为什么专门拿出来写一写,还是因为个人觉得要想把单例模式写好还真不是一件容易的事情。
其中涉及到不少编译和底层的知识。这里以Linux平台为例,这是因为本人对windows下的编程实在不太熟悉。

本文所有代码均上传至github仓库:https://github.com/kevin-shanghai/Programming_Practice
如果有兴趣,可以随意获取,本代码仅用于测试,如用于生产环境发生任何问题,本人概不负责。

本文针对于线程安全的Singleton实现,并基于模板,这样通用性更好,否则对于每个类都要实现自己的单例模式。
一般的单线程下的Singleton实现非常简单,用一个静态成员函数返回该类的一个实例就行了,但是遇到多线程,事情就麻烦了不少。
废话少说,下面看下具体代码:


/**********************************************
******* g++ test.cpp -lpthread****************
* *******************************************/
#include <iostream>
#include <pthread.h>
using namespace std;
template <class T>
class Singleton
{
public:
Singleton()
{

}
static T* GetInstance()
{
if(m_pInstance == NULL)
{
pthread_mutex_lock(&mutex);
if(m_pInstance == NULL)
{
T* temp = new T;
m_pInstance = temp;
}
pthread_mutex_unlock(&mutex);
}
return m_pInstance;
}
private:
static T* m_pInstance;
static pthread_mutex_t mutex;
};

template<class T>
T* Singleton<T>::m_pInstance = NULL;

template<class T>
pthread_mutex_t Singleton<T>::mutex;

class Test:public Singleton<Test>
{
public:
Test()
{
cout<<"Test::Test()."<<endl;
}
};

void* thread1(void*)
{
cout<<"In thread1."<<endl;
Test* test1 = Test::GetInstance();
}

void* thread2(void *)
{
cout<<"In thread2."<<endl;
Test* test2 = Test::GetInstance();
}

int main()
{
const unsigned int thread_num = 20;
pthread_t thread_id[thread_num];
for(unsigned int i = 0; i<thread_num;i++)
{
pthread_create(&thread_id[i], NULL, thread1, NULL);
}
for(unsigned int i = 0; i<thread_num;i++)
{
pthread_create(&thread_id[i], NULL, thread2, NULL);
}
sleep(1);
return 0;
}


可以看到上面的代码中使用了double-check机制,也就是对m_pInstance检查了两遍,这样有利于提高性能。还有一点需要注意的是
T* temp = new T; m_pInstance = temp;而没有直接用m_pInstance = new T;这是因为这一语句将会分为三步来执行:
1. 非配内存
2. 用相应的实例初始化该内存区域
3. 将该内存地址赋值给该指针m_pInstance
如果其中第2步骤和第3步或者第一步骤颠倒了,也就是编译器对我们的代码进行了优化,那么赋值给m_pInsannce指针的就是没有初始化的
所以我们用一个临时变量解决这个问题

其实Linux平台下的singleton模式有更简单的实现方式,主要是利用pthread_once这个api,看代码:

#include <iostream>
#include <pthread.h>
using namespace std;
template<class T>
class Singleton
{
public:
static T& instance()
{
pthread_once(&m_Once, &Singleton::init);
return *m_tValue;
}

private:
Singleton();
~Singleton();
static void init()
{
m_tValue = new T();
}

private:
static pthread_once_t m_Once;
static T* m_tValue;
};

template<class T>
pthread_once_t Singleton<T>::m_Once = PTHREAD_ONCE_INIT;

template<class T>
T* Singleton<T>::m_tValue = NULL;


class Test
{
public:
Test()
{
cout<<"In Test::Test()."<<endl;
}
};

void* thread_func(void*)
{
Test& t = Singleton<Test>::instance();
}

int main(int argc, char const *argv[])
{
// Test& t = Singleton<Test>::instance();
pthread_t thread_id[10];
for(int i=0;i<10;i++)
{
pthread_create(&thread_id[i], NULL, thread_func, NULL);
}
return 0;
}


其实这种方式在Linux平台下比较简单,pthread_once保证Init函数只执行一次,建议Linux下大家用这种方法来实现。
这种单件模式比较通用,而且在多线程环境下也很稳定,如果大家要实现跨平台的singleton模式,那么可以加上相应平台的
创建线程的api就可以了。