Posted on 2010-03-12 14:33
rikisand 閱讀(250)
評論(0) 編輯 收藏 引用
首先,可重入和線程安全是兩個并不等同的概念,一個函數可以是可重入的,也可以是線程安全的,可以兩者均滿足,可以兩者皆不滿組(該描述嚴格的說存在漏洞,參見第二條)。
其次,從集合和邏輯的角度看,可重入是線程安全的子集,可重入是線程安全的充分非必要條件。可重入的函數一定是線程安全的,然過來則不成立。
第三,POSIX 中對可重入和線程安全這兩個概念的定義:
Reentrant Function:
A function whose effect, when called by two or more threads,is guaranteed to be as if the threads each executed thefunction one after another in an undefined order, even ifthe actual execution is interleaved.
From IEEE Std 1003.1-2001 (POSIX 1003.1)
-- Base Definitions, Issue 6
Thread-Safe Function:
A function that may be safely invoked concurrently by multiple threads.
另外還有一個 Async-Signal-Safe的概念
Async-Signal-Safe Function:
A function that may be invoked, without restriction fromsignal-catching functions. No function is async-signal -safe unless explicitly described as such.
以上三者的關系為:
Reentrant Function 必然是Thread-Safe Function和Async-Signal-Safe Function
可重入與線程安全的區別體現在能否在signal處理函數中被調用的問題上,可重入函數在signal處理函數中可以被安全調用,因此同時也是Async-Signal-Safe Function;而線程安全函數不保證可以在signal處理函數中被安全調用,如果通過設置信號阻塞集合等方法保證一個非可重入函數不被信號中斷,那么它也是Async-Signal-Safe Function。
值得一提的是POSIX 1003.1的System Interface缺省是Thread-Safe的,但不是Async-Signal-Safe的。Async-Signal-Safe的需要明確表示,比如fork ()和signal()。
最后讓我們來構想一個線程安全但不可重入的函數:
假設函數func()在執行過程中需要訪問某個共享資源,因此為了實現線程安全,在使用該資源前加鎖,在不需要資源解鎖。
假設該函數在某次執行過程中,在已經獲得資源鎖之后,有異步信號發生,程序的執行流轉交給對應的信號處理函數;再假設在該信號處理函數中也需要調用函數func(),那么func()在這次執行中仍會在訪問共享資源前試圖獲得資源鎖,然而我們知道前一個func()實例已然獲得該鎖,因此信號處理函數阻塞——另一方面,信號處理函數結束前被信號中斷的線程是無法恢復執行的,當然也沒有釋放資源的機會,這樣就出現了線程和信號處理函數之間的死鎖局面。
因此,func()盡管通過加鎖的方式能保證線程安全,但是由于函數體對共享資源的訪問,因此是非可重入。