C++实现线程安全的单例模式
C++實現線程安全的單例模式
1、單例模式
一個簡單的單例模式很容易實現:構造函數聲明為private或protect防止被外部函數實例化,內部保存一個private static的類指針保存唯一的實例,實例的動作由一個public的類方法代勞,該方法也返回單例類唯一的實例。
class singleton { protected:singleton(){} private:static singleton* p; public:static singleton* instance(); }; singleton* singleton::p = NULL; singleton* singleton::instance() {if (p == NULL)p = new singleton();return p; }上面這種簡單的方式即是懶漢模式的單例模式,但是這種方法是線程不安全的,考慮兩個線程同時首次調用instance方法且同時檢測到p是NULL值,則兩個線程會同時構造一個實例給p,這是嚴重的錯誤!同時,這也不是單例的唯一實現!
? ? ? ?
class HDRNet { private:HDRNet();static HDRNet* hdrnet; public:static HDRNet* get_hdrnet();~HDRNet(); };?
#include "hdrnet.h"HDRNet * HDRNet::hdrnet = nullptr; HDRNet * HDRNet::get_hdrnet() {if (hdrnet==nullptr){hdrnet = new HDRNet();}return hdrnet; } HDRNet::HDRNet() { }HDRNet::~HDRNet() {if (hdrnet != nullptr){delete hdrnet;} }2、懶漢與餓漢
? ? ?單例大約有兩種實現方法:懶漢與餓漢。
- 懶漢:故名思義,不到萬不得已就不會去實例化類,也就是說在第一次用到類實例的時候才會去實例化,所以上邊的經典方法被歸為懶漢實現;
- 餓漢:餓了肯定要饑不擇食。所以在單例類定義的時候就進行實例化。
? ? ?特點說明:
- 由于要進行線程同步,所以在訪問量比較大,或者可能訪問的線程比較多時,采用餓漢實現,可以實現更好的性能。這是以空間換時間。
- 在訪問量較小時,采用懶漢實現。這是以時間換空間。
3 、線程安全的懶漢實現
簡單的懶漢模式是線程不安全的,一個簡單的方法是:加鎖。這里使用的線程庫是Pthread。
? ? ? ?POSIX線程(POSIX threads),簡稱Pthreads,是線程的POSIX標準。Pthread是由POSIX提出的一套通用的線程庫,在linux平臺下,它被廣泛的支持,而在windows平臺下,需要下載pthreads-w32庫。之所以選擇Pthreads庫作為多線程處理庫,是因此Android NDK開發時,可以使用pthread在C++中實現多線程處理。
pthreads-w32在Windows的配置方法,可參考:
https://blog.csdn.net/qianchenglenger/article/details/16907821
pthreads-w32-2-9-1:https://download.csdn.net/download/guyuealian/10623897
其他版本的地址:ftp://sourceware.org/pub/pthreads-win32
關于pthread的用法,可參考這個PDF:
https://download.csdn.net/download/qq_21792169/9380962
-
方法1:加鎖的經典懶漢實現(線程安全):
-
方法2:內部靜態變量的懶漢實現
此方法也很容易實現,在instance函數里定義一個靜態的實例,也可以保證擁有唯一實例,在返回時只需要返回其指針就可以了。推薦這種實現方法,真得非常簡單。
class singleton { protected:singleton(){pthread_mutex_init(&mutex);} public:static pthread_mutex_t mutex;static singleton* initance();int a; };pthread_mutex_t singleton::mutex; singleton* singleton::initance() {pthread_mutex_lock(&mutex);static singleton obj;pthread_mutex_unlock(&mutex);return &obj; }4、餓漢模式
簡單的懶漢模式是線程不安全的,因此需要加鎖,但餓漢模式本來就是線程安全的,所以不用加鎖啦!原因很簡單啊,因為的類實例創建(對象創建)放在類外啦,因此只有唯一的一個啦。
class singleton { protected:singleton(){} private:static singleton* p; public:static singleton* initance(); }; singleton* singleton::p = new singleton; singleton* singleton::initance() {return p; }5、實際應用
? ? 下面懶漢模式的代碼,也是我工程中常用的一種方法:
頭文件Singleton.h:
#ifndef _SINGLETON_H #define _SINGLETON_H #define PTHREAD_ON #ifdef PTHREAD_ON #include "pthread.h" #endif // PTHREAD_H #include <string> using namespace std; class singleton { private:singleton(string str);static singleton *p;//靜態成員變量不依賴于任何對象,需要在類外單獨分配空間。 public:~singleton();void fun(int a);static singleton* getInstance(string str);static pthread_mutex_t mtx;}; #endif實現文件:Singleton.cpp
#include "Singleton.h" #include <iostream> using namespace std;pthread_mutex_t singleton::mtx; singleton* singleton::p = nullptr;//靜態成員變量不依賴于任何對象,需要在類外單獨分配空間。 singleton::singleton(string str) {printf("構造函數:%s\n",str.c_str());pthread_mutex_init(&mtx, 0); }singleton::~singleton() {printf("~析構函數\n");if (mtx){printf("pthread_mutex_destroy\n");pthread_mutex_destroy(&mtx);}if (p){p = nullptr;} }void singleton::fun(int a) {printf("調用函數:fun,a=%d\n", a); }singleton* singleton::getInstance(string str) {printf("getInstance\n");if (p == NULL){pthread_mutex_lock(&mtx);//上鎖p = new singleton(str);//懶漢模式pthread_mutex_unlock(&mtx);//解鎖}return p; }main.cpp主程序:
#include "Singleton.h"int main() {//singleton* singleton = new singleton();//構成函數被聲明為私有,從而避免任意創建singleton* singleton1 = singleton::getInstance("Alian");singleton* singleton2 = singleton::getInstance("Blian");if (singleton1 == singleton2) {fprintf(stderr, "singleton1 = singleton2\n");printf("singleton1:%d\n", singleton1);printf("singleton2:%d\n", singleton2);}singleton1->fun(1);singleton2->fun(2);delete singleton1;singleton1 = nullptr;printf("main\n");return 0; }?
總結
以上是生活随笔為你收集整理的C++实现线程安全的单例模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: OpenCV使用pthread实现多线程
- 下一篇: Android使用NDK OpenGL