跳至內容

執行緒安全

本頁使用了標題或全文手工轉換
維基百科,自由的百科全書

執行緒安全程式設計中的術語,指某個函式函式庫多執行緒環境中被呼叫時,能夠正確地處理多個執行緒之間的公用變數,使程式功能正確完成。

假設有間銀行只有 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;
}

外部連結

[編輯]