可重入函數與線程安全

重入:當不同的控制流程調用同一個函數時,有可能當一個調用還沒返回時,另一個調用就進入了該函數,將其稱作重入;

不可重入函數:當一個函數由於可重入出錯,則將此函數稱爲不可重入函數;

可重入函數:當一個函數由於可重入而不影響各自流程的正確結果(其只訪問自己的局部變量和參數),將此函數稱作可重入函數;

舉例:常見情況中,當程序正在執行到某一個函數中的某一條指令時,這時其收到了一個信號,於是其暫停執行去處理信號,執行此信號的處理動作,而這個信號處理動作的函數正好爲剛剛執行到函數,於是就對此函數造成了可重入,而當信號處理動作執行此函數正確運行,程序返回時之前暫停執行的此函數也能正確運行,則將此函數稱爲可重入函數。

注意:重入前提條件是不同的控制流程訪問同一個函數,而信號處理函數和main函數正是使用不同的堆棧空間,它們之間不存在調用和被調用的關係,就是兩個獨立的控制流程。

確保可重入函數的必要條件如下:

(1)不在函數內部使用靜態和全局數據(由於進程不同的控制流程訪問共享數據會造成數據錯亂);

(2)不調用malloc或free,因爲malloc也是用全局鏈表來管理堆的;

(3)不調用標準I/O庫函數,標準I/O庫的很多實現都以不可重入的方式使用全局數據結構;

(4)不調用不可重入函數;

(5)不能返回靜態和全局數據,所有數據都由函數的調用者提供;

(6)使用本地數據、或通過製作全局數據的本地拷貝來保護全局數據;

(7)若必須訪問全局數據,利用互斥機制保護全局數據;

(8)不使用靜態的數據結構。

可重入函數特點:

(1)可重入函數調用多次不會出錯,其不用擔心數據被破壞;

(2)可重入函數在任意時刻被中斷,一段時間恢復,其數據不會丟失;

(3)只使用局部變量,保存在CPU寄存器或棧中,或者使用全局變量但會予以保護。


線程安全:

若一個函數被稱爲線程安全的,當且僅被多個併發線程反覆調用時,它會一直產生正確的結果。

確保線程安全:

確保線程安全,主要考慮線程之間的共享數據,屬於同一個進程之間的線程會共享進程內存空間的全局區和堆,而其私有的主要爲棧空間、上下文(寄存器等),所以對於每個線程其局部變量是私有的,全局變量、局部靜態變量、全局靜態變量、分配與堆的變量都是共享的,若對這些共享數據訪問時,確保其線程安全,則必須通過加鎖等方式使線程間互斥的訪問,不然必定造成數據的錯亂,如共享數據的值由於不同線程的訪問發生變化產生不可預料的後果,可能導致整個進程異常。


可重入函數與線程安全關係:


1.線程安全是在多個線程情況下引發的,可重入函數可以在只有一個線程情況下來說;

2.可重入函數一定爲線程安全的,但線程安全函數不一定爲可重入函數,例如:

(1).若一個函數使用了全局和靜態變量,其不是線程安全和可重入函數;

(2).若此函數使用了全局和靜態變量,但加上了互斥鎖等,則其爲線程安全的,其保證不同線程之間互斥的訪問共享數據,但沒有限制同一個線程,所以其不是可重入的;

(3).若此函數只使用了其局部變量和參數,則其爲線程安全與可重入的。

3.可重入函數比不可重入線程安全函數效率高一些;

4.線程安全函數能夠使不同線程訪問同一塊地址空間,可重入函數要求不同的執行流對數據的操作互不影響使結果是相同的。


總結:當一個函數被多個線程調用時一般不會使用任何共享數據,這個函數就是可重入的,線程安全的根源在於共享數據,所以不共享任何數據的可重入函數必定爲線程安全的,但線程安全可以通過同步與互斥機制來保證共享數據的使用,所以線程安全不一定爲可重入的。

常見的信號處理函數是不能使用不可重入函數的。




發佈了85 篇原創文章 · 獲贊 56 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章