乙個執行緒安全的單例模式測試

2021-08-26 15:02:50 字數 3266 閱讀 3654

單例模式,一般我喜歡這樣實現

class singletest

;singletest *singletest::instance()

singletest::singletest()

singletest::~singletest()

然後這樣用

singletest *ts =singletest::instance();
這麼實現的乙個好處就是比較簡單,而且不會有記憶體洩露。但如果在多執行緒環境下是否安全呢?多執行緒環境下可能會有兩種情況:

第一,如果兩個執行緒同時呼叫singletest *ts = singletest::instance();,如果執行緒一先執行構造,但還沒構造完,執行緒二構造的時候發現還沒有構造例項,會再次執行構造,單例就變成了兩例。

第二,如果兩個執行緒都要對成員變數進行讀寫,那麼會不會發生競爭呢?

理論分析一下:

第一種情況,c++11標準的編譯器是執行緒安全的,c++11標準要求編譯器保證static的執行緒安全。而c++11之前標準的編譯器則是不確定,關鍵看編譯器的實現。

第二種情況,任何標準下都不是執行緒安全的。

第一種情況,因為有標準的硬性規定,倒是不需要測試了。那麼第二種情況什麼樣?寫個**測試一下

#include 

#include

#include

class singletest

;singletest *singletest::instance()

void singletest::test()

}int singletest::get()

singletest::singletest()

singletest::~singletest()

void *threadfunc(void *arg)

int main(int argc, char* argv)

singletest::test()函式執行了5000次累加,兩個執行緒同時呼叫該函式,如果是執行緒安全的,最後的結果應該是10000,如果執行緒是不安全的,最後的結果應該不確定。

經過測試,最後的結果也確實是不確定的,說明的確是執行緒不安全。

既然執行緒不安全,那麼加個鎖會是什麼樣?**加個鎖,再試一下。

class singletest

;singletest *singletest::instance()

void singletest::test()

}int singletest::get()

singletest::singletest()

singletest::~singletest()

經過測試,這樣就執行緒安全了。

但新的問題又來了,如果有多個成員變數,是乙個變數加乙個鎖?還是用同乙個鎖?

如果用同乙個鎖,那麼每次對成員變數進行讀寫的時候都要上鎖。乙個執行緒需要訪問成員變數a,先上鎖,另乙個執行緒要訪問b,此時a和b是沒有發生競爭的,但由於用了同乙個鎖,那麼b也要等著a將鎖釋放後才能進行操作。

如果乙個變數用乙個鎖,倒是不會發生之前的那種無必要的資源浪費,但鎖多了難免麻煩也就多了。

這就是乙個取捨問題了。

還有一種方案,把鎖放在類的外面,由執行緒函式去處理鎖

static pthread_mutex_t mutex = pthread_mutex_initializer;

class singletest

;singletest *singletest::instance()

void singletest::test()

}int singletest::get()

singletest::singletest()

singletest::~singletest()

void *threadfunc(void *arg)

這樣也是執行緒安全的,但也有乙個問題,類的外面並不知道究竟哪個成員函式需要上鎖,為了安全,每次呼叫成員函式都要上鎖,還是會存在資源浪費的情況。

看來,這種單例的實現方式也與不爽的地方,而且,如果是c++11之前的編譯器,構造的執行緒安全性也是不確定的。

如果是c++11之前的編譯器,可以這樣實現

class singletest

;singletest singletest::st;

singletest *singletest::instance()

void singletest::test()

}int singletest::get()

singletest::singletest()

singletest::~singletest()

這種方法有個缺陷,就是如果構造的時候需要傳入引數,這種方法就不行了,而且也存在成員變數鎖的問題。

再試試下面這種實現

class singletest}};

int m;

static singletest* m_instance;

pthread_mutex_t m_mutex;

static pthread_mutex_t m_insmutex;

static cgarbo garbo;

};pthread_mutex_t singletest::m_insmutex = pthread_mutex_initializer;

singletest* singletest::m_instance = null;

singletest::cgarbo singletest::garbo;

singletest *singletest::instance()

s = pthread_mutex_unlock(&m_insmutex);

if (s != 0)

printf("unlock error\n");

}return m_instance;

}void singletest::test()

}int singletest::get()

singletest::singletest()

singletest::~singletest()

這種實現任何標準下都是構造執行緒安全的,也不會有記憶體洩露,同時也支援構造時輸入,但實現起來太麻煩,單單構造都需要乙個鎖。

但成員變數鎖的問題還是存在的,一直沒有找到比較完美的方法。

手寫乙個執行緒安全的單例工廠模式

簡單介紹一下倆個模式 單例模式 乙個類只會被產生乙個靜態的物件。工廠模式 構造方法不對外提供。提供乙個方法,包括產生物件和對物件的初始化。同時保證多執行緒獲取物件時,獲取的是同乙個物件。public class resource public resource newinstance return ...

執行緒安全的單例模式

廢話不多說,常用的 積澱下來。一 懶漢模式 即第一次呼叫該類例項的時候才產生乙個新的該類例項,並在以後僅返回此例項。需要用鎖,來保證其執行緒安全性 原因 多個執行緒可能進入判斷是否已經存在例項的if語句,從而non thread safety.使用double check來保證thread safe...

執行緒安全的單例模式

單例模式是為了保證乙個類只有乙個例項而且易於外界訪問。所以一般只有把建構函式,拷貝函式,析構函式,賦值函式,變數名變為私有。再用乙個get函式訪問提供介面即可。考慮執行緒安全就要加鎖。一 懶漢模式 1 靜態成員例項的懶漢模式 class singleton public static singlet...