可重入函數對於線程安全的意義(附函數表)

是什麼

什麼是可重入函數,什麼是不可重入函數?

不可重入函數:
在併發服務器中,經常會出現多個任務調用同一個函數的情況,比方說後端服務器使用多線程同時對數據庫進行訪問操作。如果有一個函數不幸被設計成爲這樣:那麼不同任務調用這個函數時可能修改其他任務調用這個函數的數據,從而導致不可預料的後果。這樣的函數是不安全的函數,也叫不可重入函數。
(其實也沒什麼不可預料的,就是服務器崩了唄,然後我就完了唄)

可重入函數:
所謂可重入是指一個可以被多個任務調用的過程,任務在調用時不必擔心數據是否會出錯。
一個可重入的函數簡單來說就是可以被中斷的函數。

可重入函數的分類
(1)顯式可重入函數
如果所有函數的參數都是傳值傳遞的(沒有指針),並且所有的數據引用都是本地的自動棧變量(也就是說沒有引用靜態或全局變量),那麼函數就是顯示可重入的,也就是說不管如何調用,我們都可斷言它是可重入的。
(2)隱式可重入函數
可重入函數中的一些參數是引用傳遞(使用了指針),也就是說,在調用線程小心地傳遞指向非共享數據的指針時,它纔是可重入的。
可重入函數可以有多餘一個任務併發使用,而不必擔心數據錯誤,相反,不可重入函數不能由超過一個任務所共享,除非能確保函數的互斥(或者使用信號量,或者在 代碼的關鍵部分禁用中斷)。可重入函數可以在任意時刻被中斷,稍後再繼續運行,不會丟失數據,可重入函數要麼使用本地變量,要麼在使用全局變量時保護自己 的數據。

爲什麼

爲什麼有的函數可重入,又有的函數不可重入?

  1. 爲什麼可重入

一個可重入函數可以被多個執行流重複進入,內部使用的數據都應該來自於自身的棧空間,包括返回值也不應該是全局或者靜態的,可以允許有該函數的多個副本在運行,而正是因爲其中的操作數據都來自於自身的棧空間,而每次調用函數會開闢不同的棧空間,因此二者互不影響。

  1. 爲什麼不可重入
    不可重入的特點:如果一個函數符合以下條件之一則是不可重入的
    (1)調用了malloc/free函數,因爲malloc函數是用全局鏈表來管理堆的
    (2)調用了標準I/O庫函數,標準I/O庫的很多實現都是以不可重入的方式使用全局的數據結構
    (3)可重入體內使用了靜態的數據結構

怎麼做

  1. 任何確保寫的函數可重入

1.不在函數內部使用靜態或全局變量
2.不返回靜態或全局變量,所有數據都有函數的調用者提供。
3.使用本地數據,或通過製作全局數據的本地拷貝來保護全局數據
4.不調用不可重入函數

如果確實需要訪問全局變量(包括 static),一定要注意實施互斥手段。可重入函數在並行運行環境中非常重要,但是一般要爲訪問全局變量付出一些性能代價。
編寫可重入函數時,若使用全局變量,則應通過關中斷、信號量(即P、V操作)等手段對其加以保護。

舉例

Linux下常見可重入函數
在這裏插入圖片描述
上面那個不夠滿足的話,看下面這個:

POSIX.1-20001標準規定,所有的標準庫函數都必須是可重入函數,除了以下這些:
在這裏插入圖片描述

當然,咱自己寫出不可重入函數就另當別論了。

其他

線程安全與可重入函數的區別
(1)、可重入函數是線程安全函數的一種,其特點在於它們被多個線程調用時,不會引用任何共享數據。
(2)、線程安全是在多個線程情況下引發的,而可重入函數可以在只有一個線程的情況下來說。
(3)、線程安全不一定是可重入的,而可重入函數則一定是線程安全的。
(4)、如果一個函數中有全局變量,那麼這個函數既不是線程安全也不是可重入的。
(5).如果將對臨界資源的訪問加上鎖,則這個函數是線程安全的,但如果這個重入函數若鎖還未釋放則會產生死鎖,因此是不可重入的。
(6)、線程安全函數能夠使不同的線程訪問同一塊地址空間,而可重入函數要求不同的執行流對數據的操作互不影響使結果是相同的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章