本文描述3种场景下的单例模式:
- 进程体内无线程的单例模式
- 进程体内多线程单例模式
- 在单个线程体中的单例模式
本文所写单例模式代码都使用懒汉模式。
进程体内单例
注意问题:
- 如果进程体中运行多个线程,则需要考虑多线程同步访问的问题。
- 如果进程体重没有运行多个线程,则不需要考虑多线程同步访问。
- 使用线程同步锁保证多进程同步
使用场景举例 :
- 日志类、文件读写类
- 资源管理类
代码示例:
进程体内没有运行多线程的单例模式,无需考虑线程同步与互斥
class Singleton {
public:
static Singleton* getInstance() {
if (NULL == instance) {
instance = new SingletonInside();
}
return instance;
}
private:
SingletonInside(){}
~SingletonInside() {}
static Singleton* instance;
};
Singleton::instance = NULL;
进程体内运行多线程单例模式,使用系统mutex保证线程安全
class Singleton {
public:
static Singleton* getInstance() {
pthread_once(&g_once_control, InitOnce);
pthread_mutex_lock(&mutex); // lock
if (NULL == instance) {
instance = new SingletonInside();
}
pthread_mutex_unlock(&mutex); // unlock
return instance;
}
private:
SingletonInside() {
}
~SingletonInside() {
pthread_mutex_destroy(&mutex); // destroy lock
}
static void InitOnce(void) {
pthread_mutex_init(&mutex,NULL); // init lock
}
Singleton* instance;
static pthread_once_t g_once_control;
static pthread_mutex_t mutex;
};
Singleton::instance = NULL;
pthread_once_t Singleton::g_once_control = PTHREAD_ONCE_INIT;
单个线程体中的单例
某些资源在单个线程体内需要保持单例,即每个线程体内都保持唯一。每个线程体内,对象相互隔离,则无需考虑线程安全问题。
此种单例模式的实例需要调用系统提供的 TLS 接口,放于线程局部存储中。
使用场景举例:
- 多路复用Socket封装类
- 网络上下文环境类
- 线程安全的资源
代码示例
class Singleton {
public:
static Singleton* getInstance() {
pthread_once(&g_once_control, InitOnce);
Singleton* instance = (Singleton*)pthread_getspecific(g_thread_data_key);
if (NULL == instance) {
instance = new SingletonInside();
pthread_setspecific(g_thread_data_key, (void*)Singleton)
}
return instance;
}
private:
SingletonInside() {}
~SingletonInside() {
pthread_key_delete(g_thread_data_key);
}
static void InitOnce(void) {
pthread_key_create(&g_thread_data_key, NULL);
}
static pthread_once_t g_once_control;
static pthread_key_t g_thread_data_key;
};
pthread_once_t Singleton::g_once_control = PTHREAD_ONCE_INIT;
pthread_key_t Singleton::g_thread_data_key;
如果使用 Poco库 TreadLocal ,代码还会简洁很多,性能上也会好很多
class Singleton {
public:
static Singleton* getInstance() {
if (NULL == instance) {
instance = new SingletonInside();
}
return instance;
}
private:
SingletonInside() {}
~SingletonInside() {
delete instance;
}
static Poco::ThreadLocal<Singleton> instance;
};
Poco::ThreadLocal<singleton> Singleton::instance = NULL;
总结
- 无论程序是多进程运行还是多线程运行,代码都要尽量兼容线程安全
Pingback引用通告: 工程师手记-将多进程后台服务改造为多线程 | 作程的技术博客