執行緒安全與可重入函式的區別與聯絡

2021-08-20 03:22:57 字數 3233 閱讀 5988

執行緒安全是多個執行緒訪問時,採用了加鎖機制,當乙個執行緒訪問該類的某個資料時,進行保護,其他執行緒不能進行訪問直到該執行緒讀取結束並且釋放了鎖,其他執行緒才可使用,保證了資料的一致性。

與之對應的則是執行緒不安全,對資料的訪問不提供保護機制,導致多個執行緒先後更改資料造成資料的不一致問題,這是乙個非常嚴重的問題。一般來說,乙個函式被稱為執行緒安全的,當且僅當被多個執行緒反覆呼叫時,它會一直產生正確的結果。

下面我們來看乙個多執行緒的i++操作:

#include#include#include#includeint count = 0;

void *pthread_run(void *arg)

return null;

}int main()

i++操作我們只需要三步操作:

(1)讀取i到某個暫存器中;

(2)i++;

(3)將暫存器中的內容寫回記憶體;

執行結果如下:

從實驗結果可以看出,兩個執行緒併發執行,程式在使用者態與核心態之間不斷切換,兩個執行緒對其進行訪問,就會造成資料的不一致問題。

如果你知道i++的彙編**你就會發現,i++操作並不是一條指令完成的,即不是原子的。作業系統執行i++的時候可能執行了一部分就被排程去執行另乙個**,而單條指令是不會被打斷的。

解決方式:對臨界區加鎖(二元訊號量、互斥鎖、多元訊號量等)

下面我們使用互斥鎖對臨界區進行加鎖保護:

#include#include#include#include//互斥鎖的初始化

pthread_mutex_t mutex = pthread_mutex_initializer;

int count = 0;

void *pthread_run(void *arg)

return null;

}int main()

執行結果如下:

這樣一來,就保證了多執行緒訪問時資料的一致性,當然這只是執行緒安全的冰山一角。

我們能夠定義出四個(不相交的)執行緒不安全函式類:

(1)不保護共享變數的函式。

比如上述操作中的i++操作。

解決方法:對臨界區加鎖,或者使用pv操作的訊號量來保護共享的變數。

(2)保持跨越多個呼叫的狀態函式。

比如乙個偽隨機數生成器,當呼叫srand為rand設定乙個種子後,如果多執行緒呼叫rand函式,就會造成執行緒的安全隱患。

解決方法:重寫rand函式,使得它不再使用任何static資料,而是依靠呼叫者在引數中傳遞狀態資訊。

(3)返回指向靜態變數的指標的函式。

比如將乙個計算結果放在乙個static變數中,然後返回乙個指向這個變數的指標。如果多執行緒呼叫這些函式,正在被乙個執行緒使用的結構會被另乙個執行緒覆蓋掉。

解決方法:① 選擇重寫函式,使得呼叫者傳遞存放結果的變數的位址,消除了所有共享資料。 ② 使用加鎖-拷貝(lock-and-copy)技術。將執行緒不安全函式與互斥鎖聯絡起來,在每乙個呼叫位置,對互斥鎖加鎖,呼叫執行緒不安全函式,將函式返回的結果拷貝到乙個私有的儲存器位置,然後對互斥鎖解鎖。

(4)呼叫執行緒不安全函式的函式。

我們假設函式a安全,函式b不安全。

情況①:如果函式a呼叫b,那麼a不一定不安全。如果b是第(2)類的函式,即依賴於跨越多次呼叫的狀態,那麼a執行緒肯定不安全。解決方法:對函式b進行重寫。

情況②:如果b是第(1)類或者第(3)類。解決方法:需要用互斥鎖保護呼叫位置和任何得到的共享資料,a仍可能是執行緒安全的。

重入:即重複呼叫,函式被不同的流呼叫,有可能會出現第一次呼叫還沒有返回時就再次進入該函式開始下一次呼叫。

可重入:當程式被多個執行緒反覆執行,結果總是正確的。

不可重入:當程式被多個執行緒反覆呼叫,產生的結果會出錯。

可重入函式:可以重複進入。這個函式不僅可以被中斷,而且除了使用自己棧上的變數以外不依賴於任何環境(包括static)。可以允許有多個函式的副本在執行,由於它們使用的是分離的棧,因此不會互相干擾。

不可重入函式:一重入就會出錯。由於使用了一些系統資源,比如全域性變數區,中斷向量表等,如果被中斷,是不能在多工環境下生存的。

可重入的特點:由於可重入函式被多次呼叫不會出錯,因此可重入函式不用擔心資料會被破壞。可重入函式任何時候都可以被中斷,一段時間後又可以繼續執行,而相應的資料不會丟失。可重入函式若只使用區域性變數,即儲存在cpu暫存器或者堆疊中,如果使用的是全域性變數,則要對全域性變數予以保護。

不可重入的特點:如果乙個函式符合以下條件之一的,則是不可重入的:

(1)呼叫了 malloc/free 函式,因為 malloc 函式是用全域性鍊錶來管理的。

(2)呼叫了標準i/o庫函式,標準i/o庫的很多實現都以不可重入的方式適用全域性資料結構。

(3)可重入體內使用了靜態的資料結構。

很多時候,可重入函式與執行緒安全被用作同義詞,但是它們還是有很明顯的區別的,可重入函式僅僅是執行緒安全函式的乙個真子集,如下圖所示:

乙個函式要想被重入,只有以下兩種情況:

(1)多個執行緒同時執行這個函式;

(2)函式自身(可能是經過多層呼叫之後)呼叫本身;

乙個函式之所以可重入,則表明了重入對該函式不會造成任何不良的影響。

乙個函式稱為可重入的充要條件:

(1)不是任何(區域性)靜態或全域性的非const變數;

(2)不返回任何(區域性)靜態或全域性的非const變數的指標;

(3)僅依賴於呼叫方提供的引數;

(4)不依賴任何單個資源的鎖;

(5)不呼叫任何不可重入函式;

可重入是併發安全的強力保障,乙個可重入的函式可以在多執行緒環境下放心使用。

(1)執行緒安全不一定是可重入的,而可重入函式一定是執行緒安全的。

(2)執行緒安全是多個執行緒下引起的,但可重入函式可以在只有乙個執行緒的情況下發生。

(3)若乙個函式中存在全域性變數,那麼這個函式既不是執行緒安全的也不是可重入的。

(4)執行緒安全函式能夠使不同的執行緒訪問同一塊位址空間,而可重入函式要求不同的執行流對資料的操作互不影響結果是相同的。

執行緒安全與可重入函式

可重入函式 reentrant function 與執行緒安全函式 thread safe function 有時容易混淆,而且各種文件中的解釋也不是很清楚,這裡根據筆者的經驗來說明一下。執行緒安全函式 概念 執行緒安全的概念比較直觀。一般說來,乙個函式被稱為執行緒安全的,當且僅當被多個併發執行緒反...

可重入函式與執行緒安全

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

可重入函式與執行緒安全

可重入函式與執行緒安全 執行緒安全 假如在乙個函式中它是這麼寫的,在乙個全域性鍊錶上存放資料,在單執行緒模式下,我們先new乙個新的節點然後讓head next指向這個節點,這種場景在多執行緒場景下會是這樣的過程,執行緒一new了乙個節點,然後cpu轉去執行執行緒二,執行緒二new乙個節點後head...