安全執行緒函數和可再入函數
3.1安全執行緒函數
(1)概念
安全執行緒的概念比較直觀。一般說來,一個函數被稱為安全執行緒的,若且唯若被多個並發線程反覆調用時,它會一直產生正確的結果。
(2) 確保安全執行緒
要確保函數安全執行緒,主要需要考慮 的是線程之間的共用變數。屬於同一進程的不同線程會共用進程記憶體空間中的全域區和堆,而私人的線程空間則主要包括棧和寄 存器。因此,對於同一進程的不同線程來說,每個線程的局部變數都是私人的,而全域變數、局部靜態變數、分配於堆的變數都是共用的。在對這些共用變數進行訪 問時,如果要保證安全執行緒,則必須通過加鎖的方式。
(3) 線程不安全的後果
線程不安全可能導致的後果是顯而易見的——共用變數的值由於不同線程的訪問,可能發生不可預料的變化,進而導致程式的錯誤,甚至崩潰。
3.2可重新進入函數
(1)概念
可重新進入的概念基本沒有比較正式的完 整解釋,多數的文檔都只是說明什麼樣的情況才能保證函數可重新進入,但沒有完整定義。按照Wiki上的說法,“A computer program or routine isdescribed as reentrant if it can be safely executed concurrently; that is, theroutine can be re-entered while it is already running.”根據筆者的經驗,所謂“重入”,常見的情況是,程式執行到某個函數foo()時,收到訊號,於是暫停目前正在執行的函數,轉到訊號處理 函數,而這個訊號處理函數的執行過程中,又恰恰也會進入到剛剛執行的函數foo(),這樣便發生了所謂的重入。此時如果foo()能夠正確的運行,而且處 理完成後,之前暫停foo()也能夠正確運行,則說明它是可重新進入的。
(2)確保可重新進入
要確保函數可重新進入,需滿足以下幾個條件:
#不在函數內部使用靜態或全域資料
#不返回靜態或全域資料,所有資料都由函數的調用者提供。
#使用本機資料,或者通過製作全域資料的本地拷貝來保護全域資料。
#不調用不可重新進入函數。
(3) 不可重新進入的後果
不可重新進入的後果主要體現在象訊號處理函數這樣需要重入的情況中。如果訊號處理函數中使用了不可重新進入的函數,則可能導致程式的錯誤甚至崩潰。
3.3安全執行緒和可重新進入函數關係
可重新進入與安全執行緒並不等同。一般說來,可重新進入的函數一定是安全執行緒的,但反過來不一定成立。它們的關係可用來表示:
我們可以採用下面的變化過程來進一步說明:
- 如果一個函數中用到了全域或靜態變數,那麼它不是安全執行緒的,也不是可重新進入的;
- 如果我們對它加以改進,在訪問全域或靜態變數時使用互斥量或訊號量等方式加鎖,則可以使它變成安全執行緒的,但此時它仍然是不可重新進入的,因為通常加鎖方式是針對不同線程的訪問,而對同一線程可能出現問題;
- 如果將函數中的全域或靜態變數去掉,改成函數參數等其他形式,則有可能使函數變成既安全執行緒,又可重新進入。
比如:strtok函數是既不可重新進入的,也不是安全執行緒的;加鎖的strtok不是可重新進入的,但安全執行緒;而strtok_r既是可重新進入的,也是安全執行緒的。
3.4不能保證安全執行緒的函數