线程安全的单例模式C++实现
單例模式的介紹
單例模式的優點是只有一個對象,可以節省資源,提高運行速度,提供一個全局的訪問點。
在使用過程中,寫一個具有單例模式思想的程序可以很簡單,但是如果要做到線程安全并且生成實例的過程中符合業務需求,是一件困難的事情。
Lazy mode(懶漢模式)
懶漢模式,會將實例化的時間延遲到正式調用接口的時候。
簡單的實現方式
上面的用例中,可以看到上面的p_singletonIns的在第一次調用getInstance()的時候會將p_singletonIns賦予一個實例的地址。這種延遲到使用時才進行實例化的做法,叫做懶漢模式。
但是這種方法不是線程安全的,同時會存在內存泄露的問題。
使用指針的方式的話,在創建實例的時候一般使用DCL(Double Check Lock), 雙檢測鎖。代碼如下所示
但是在使用過程中, p1的判斷是存在一個風險,線程仍然不安全。就是在p3在new的時候,在new這個過程中,p_singletonIns的指針會被先賦值,然后開始創建內存。在并發的情況下,p1可能判斷出p_singletonIns不是空指針,但是內存其實沒有完全分配完成,那么造成返回p_singletonIns這個指針其實是不可用的。
使用原子變量的懶漢模式
在C++11的標準庫中有原子變量的實現。使用原子變量就可以上述這種避免線程不安全的問題。
std::mutex mutexIns; class singleton { public: static singleton* getInstance() {if(p_singletonIns.load() == nullptr) // p1{std::lock_guard<std::mutex> lock(mutexIns);if(p_singletonIns.load() == nullptr) // p2{p_singletonIns.store(new singleton()); // p3}}return p_singletonIns.load(); }private:singleton()=default;singleton(const singleton &cpSingleton) = delete;singleton& operator=(const singleton& cpSingleton) = delete;static std::atomic<singleton*> p_singletonIns; }; std::atomic<singleton*> singleton::p_singletonIns(nullptr);使用局部靜態變量的懶漢模式
使用局部靜態變量也可以實現懶漢模式,使用局部靜態變量在C++11之后是線程安全。這也是最為優雅的一種方式。
class singleton { public:static singleton* getInstance(){singleton singletonIns;return &singletonIns;}private:singleton()=default;singleton(const singleton &cpSingleton) = delete;const singleton& operator=(const singleton& cpSingleton) = delete; };餓漢模式
餓漢模式:故名思意,一個餓壞的人會將一切能吃的東西都塞到肚子里,吃不下去的也要拽在手里。
在編程中這種模式的體現就在于資源在程序啟動的時候就會分配好。而不是在運行時才進行資源分配。
使用餓漢模式編寫單例模式相對簡單一些。
使用餓漢模式的話,singletonIns在C++中進行初始化的過程發生在程序加載的過程中,在main運行之前,也在調用getInstance()之前。這個初始化的過程也可以知道是確保線程安全的。同時也可以保證在程序結束之后自動析構。
關于資源泄露
首先資源泄露并不只是意味著內存泄露,還會指網絡資源的釋放,比如在分布式系統中,在程序退出時,讓其他系統知道程序的退出等等場景。
在單例模式中,因為使用全局變量或者靜態變量進行構造的時候,在程序退出時,會自動進行析構,通常是不存在資源泄漏的。在使用new的情況下,則不會自動進行析構,會存在資源泄漏的問題。下面介紹使用堆內存來構造單例模式的時候,如何在程序推出時,自動析構。
使用靜態的嵌套對象來進行釋放
使用嵌套的對象變量來釋放,是因為單例模式的析構函數是private權限的,因此要使用嵌套對象來解決這個private的訪問權限的問題。也滿足高內聚編程原則。
std::mutex mutexIns; class singleton { public: class deletor { public:~deletor(){singleton *singletonIns;if((singletonIns = getInstance()) != nullptr){delete singletonIns;}} };static singleton* getInstance() {if(p_singletonIns.load() == nullptr) // p1{std::lock_guard<std::mutex> lock(mutexIns);if(p_singletonIns.load() == nullptr) // p2{p_singletonIns.store(new singleton()); // p3}}return p_singletonIns.load(); }private:singleton()=default;singleton(const singleton &cpSingleton) = delete;singleton& operator=(const singleton& cpSingleton) = delete;~singleton(){//do anything to make sure you have prevented resource leak.//...}static std::atomic<singleton*> p_singletonIns;static deletor deletorIns; }; std::atomic<singleton*> singleton::p_singletonIns(nullptr); singleton::deletor deletorIns;總結
以上是生活随笔為你收集整理的线程安全的单例模式C++实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 软件测试_入行
- 下一篇: 深入理解C++的动态绑定和静态绑定