執行緒安全
執行緒安全是程式設計中的術語,指某個函式、函式庫在多執行緒環境中被呼叫時,能夠正確地處理多個執行緒之間的公用變數,使程式功能正確完成。
假設有間銀行只有 1000 元,而兩個人同時提領 1000 元時就可能會拿到總計 2000 元的金額。為了避免這個問題,該間銀行提款時應該使用互斥鎖,即意味著針對同一個資源處理時,前一個人提領交易完成後才處理下一筆交易。但這種手法會使得效能降低。
一般來說,執行緒安全的函式應該為每個呼叫它的執行緒分配專門的空間,來儲存需要單獨儲存的狀態(如果需要的話),不依賴於「執行緒慣性」,把多個執行緒共享的變數正確對待(如,通知編譯器該變數為「易失(volatile)」型,阻止其進行一些不恰當的最佳化),而且,執行緒安全的函式一般不應該修改全域對象。
很多C庫代碼(比如某些strtok的實現,它將「多次呼叫中需要保持不變的狀態」儲存在靜態變數中,導致不恰當的共享)不是執行緒安全的,在多執行緒環境中呼叫這些函式時,要進行特別的預防措施,或者尋找別的替代方案。
例子
在下面這段代碼中,函式increment_counter是執行緒安全的,但不是可重入的。
#include <pthread.h>
int increment_counter ()
{
static int counter = 0;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&mutex);
// only allow one thread to increment at a time
++counter;
// store value before any other threads increment it further
int result = counter;
pthread_mutex_unlock(&mutex);
return result;
}
上面的代碼中,函式increment_counter可以在多個執行緒中被呼叫,因為有一個互斥鎖mutex來同步對共享變數counter的訪問。但是如果這個函式用在可重入的中斷處理程式中,如果在pthread_mutex_lock(&mutex)和pthread_mutex_unlock(&mutex)之間產生另一個呼叫函式increment_counter的中斷,則會第二次執行此函式,此時由於mutex已被lock,函式會在pthread_mutex_lock(&mutex)處阻塞,並且由於mutex沒有機會被unlock,阻塞會永遠持續下去。簡言之,問題在於 pthread 的 mutex 不可重入。
解決辦法是設定 PTHREAD_MUTEX_RECURSIVE 屬性。然而對於給出的問題而言,專門使用一個 mutex 來保護一次簡單的增量操作顯然過於昂貴,因此c++11中的原子變數提供了一個可使此函式既執行緒安全又可重入(而且還更簡潔)的替代方案:
#include <atomic>
int increment_counter ()
{
static std::atomic<int> counter(0);
// increment is guaranteed to be done atomically
int result = ++counter;
return result;
}
外部連結
- Thread-safe Tcl Extensions (頁面存檔備份,存於網際網路檔案館)(wiki page)
- Thread-safe design
- Article "Design for thread safety" by Bill Venners
- Article "Write thread-safe servlets" by Phillip Bridgham