昨天面試的時候考到了線程安全的單例模式,網上查找了相應的資料在這里總結一下:
一、懶漢模式
即第一次調用該類實例的時候才產生一個新的該類實例,並在以后僅返回此實例。
需要用鎖,來保證其線程安全性:原因:多個線程可能進入判斷是否已經存在實例的if語句,從而non thread safety。
使用double-check來保證thread safety。但是如果處理大量數據時,該鎖才成為嚴重的性能瓶頸。
1、靜態成員實例的懶漢模式
C++代碼:
class Singleton{ static Singleton * instance; static std::mutex m; Singleton(){} public: static Singleton* getInstance(); }; Singleton* Singleton::getInstance(){ if(instance == nullptr){ std::lock_guard<std::mutex> lg(m);//這里使用了std::lock_guard來達到RAII if(instance == nullptr){ instance = new Singleton(); } } return instance; } Singleton * Singleton::instance = nullptr; std::mutex Singleton::m; //靜態mutex這樣初始化我還是第一次寫。。
這一模式算是最經典的了,在普通單例模式中添加了線程安全鎖。
2、內部靜態實例的懶漢模式
class Singleton { private: Singleton(){} public: static Singleton* getInstance(){ //Lock(); //not needed after C++11 static Singleton instance; //UnLock(); //not needed after C++11 return &instance; //注意返回的是地址 } };
使用內部靜態實例我還是第一次看到,感覺挺巧妙的,而且C++11之后內部靜態變量初始化編譯器保證做到線程安全,比較方便的寫法。
二、餓漢模式
即無論是否調用該類的實例,在程序開始時就會產生一個該類的實例,並在以后僅返回此實例。
由靜態初始化實例保證其線程安全性,WHY?因為靜態實例初始化在程序開始時進入主函數之前就由主線程以單線程方式完成了初始化,不必擔心多線程問題。
故在性能需求較高時,應使用這種模式,避免頻繁的鎖爭奪。
class Singleton { private: static Singleton* instance; Singleton(){} public: static Singleton* getInstance() { return instance; } }; Singleton* Singleton::instance = new Singleton();
感覺這種實現方法挺好的,鎖都不用加。
參考:https://peach.oschina.io/post/c++_%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E7%9A%84%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F/
https://zh.cppreference.com/w/cpp/thread/lock_guard