Linux 執行緒安全和可重入函式

2021-08-03 07:28:28 字數 1584 閱讀 2949

執行緒安全:乙個函式被稱為執行緒安全的(thread-safe),當且僅當被多個併發程序反覆呼叫時,它會一直產生正確的結果。如果乙個函式不是執行緒安全的,我們就說它是執行緒不安全的(thread-unsafe)。我們定義四類(有相交的)執行緒不安全函式。

將這類執行緒不安全函式變為執行緒安全的,相對比較容易:利用像p和v操作這樣的同步操作來保護共享變數。這個方法的優點是在呼叫程式中不需要做任何修改,缺點是同步操作將減慢程式的執行時間。

乙個偽隨機數生成器是這類不安全函式的簡單例子。

unsigned int next = 1; 

int rand(void)

void srand(unsigned int seed)

rand函式是執行緒不安全的,因為當前呼叫的結果依賴於前次呼叫的中間結果。當我們呼叫srand為rand設定了乙個種子後,我們反覆從乙個單執行緒中呼叫rand,我們能夠預期乙個可重複的隨機數字序列。但是,如果有多個執行緒同時呼叫rand函式,這樣的假設就不成立了。

使得rand函式變為執行緒安全的唯一方式是重寫它,使得它不再使用任何靜態資料,取而代之地依靠呼叫者在引數中傳遞狀態資訊。這樣的缺點是,程式設計師現在要被迫改變呼叫程式的**。

某些函式(如gethostbyname)將計算結果放在靜態結構中,並返回乙個指向這個結構的指標。如果我們從併發執行緒中呼叫這些函式,那麼將可能發生災難,因為正在被乙個執行緒使用的結果會被另乙個執行緒悄悄地覆蓋了。

有兩種方法來處理這類執行緒不安全函式。一種是選擇重寫函式,使得呼叫者傳遞存放結果的結構位址。這就消除了所有共享資料,但是它要求程式設計師還要改寫呼叫者的**。

struct hostent* gethostbyname_ts(char* host)

如果函式f呼叫執行緒不安全函式g,那麼f就是執行緒不安全的嗎?不一定。如果g是類2類函式,即依賴於跨越多次呼叫的狀態,那麼f也是不安全的,而且除了重寫g以外,沒有什麼辦法。然而如果g是第1類或者第3類函式,那麼只要用互斥鎖保護呼叫位置和任何得到的共享資料,f可能仍然是執行緒安全的。比如上面的gethostbyname_ts。

可重入函式:可重入函式是執行緒安全函式的一種,其特點在於它們被多個執行緒呼叫時,不會引用任何共享資料。

可重入函式通常要比不可重入的執行緒安全函式效率高一些,因為它們不需要同步操作。更進一步說,將第2類執行緒不安全函式轉化為執行緒安全函式的唯一方法就是重寫它,使之可重入。

下面為rand函式的乙個可重入版本

int rand_r(unsigned int* nextp)

顯式可重入函式:如果所有函式的引數都是傳值傳遞的(沒有指標),並且所有的資料引用都是本地的自動棧變數(也就是說沒有引用靜態或全域性變數),那麼函式就是顯示可重入的,也就是說不管如何呼叫,我們都可斷言它是可重入的。

隱式可重入函式:可重入函式中的一些引數是引用傳遞(使用了指標),也就是說,在呼叫執行緒小心地傳遞指向非共享資料的指標時,它才是可重入的。例如rand_r就是隱式可重入的。

我們使用可重入(reentrant)來包括顯式可重入函式和隱式可重入函式。然而,可重入性有時是呼叫者和被呼叫者共有的屬性,並不只是被呼叫者單獨的屬性

執行緒安全和可重入函式

執行緒安全 當多個併發執行緒執行同乙個函式,我們都能得到正確的返回值。當多個執行緒併發的呼叫乙個函式。如果對全域性資料或者靜態資料在不加任何鎖以及安全性的處理情況下,就會對多次修改資料的錯誤。比如我正在願意個執行緒裡處理乙個全域性變數的 1 正減完。結果還沒有返回,就被另乙個執行緒切出去了,而那個執...

執行緒安全和可重入函式

執行緒安全 乙個函式稱為執行緒安全,當且僅當被多個併發執行緒反覆呼叫時,它一直產生正確的結果。如果乙個函式不是執行緒安全的,我們就說它是執行緒不安全的。以下我們定義4種,執行緒不安全的函式 第1類 不保護共享變數的函式,比如對乙個全域性變數多個執行緒操作,產生錯誤結果 解決方法 利用像p,v操作這樣...

執行緒安全和可重入函式

簡單來說,在執行環境一致的情況下,多次執行同乙個多執行緒程式時,所有執行緒結果和單執行緒執行得到的結果一致時,則可以說該程式的執行緒是安全的。反之,則為執行緒不安全。引起執行緒安全問題的原因 通常都是由全域性變數及靜態變數的。若每個執行緒中對全域性變數 靜態變數只有讀操作,而無寫操作,一般來說,這個...