線程安全和可重入的區別

源出處:http://waret.iteye.com/blog/744169


線程安全函數

  • 概念:
    線程安全的概念比較直觀。一般說來,一個函數被稱爲線程安全的,當且僅當被多個併發線程反覆調用時,它會一直產生正確的結果。

  • 確保線程安全:
    要確保函數線程安全,主要需要考慮的是線程之間的共享變量。屬於同一進程的不同線程會共享進程內存空間中的全局區和堆,而私有的線程空間則主要包括棧和寄存器。因此,對於同一進程的不同線程來說,每個線程的局部變量都是私有的,而全局變量、局部靜態變量、分配於堆的變量都是共享的。在對這些共享變量進行訪 問時,如果要保證線程安全,則必須通過加鎖的方式。

  • 線程不安全的後果:
    線程不安全可能導致的後果是顯而易見的——共享變量的值由於不同線程的訪問,可能發生不可預料的變化,進而導致程序的錯誤,甚至崩潰。


可重入函數

  • 概念:
    可重入的概念基本沒有比較正式的完整解釋,多數的文檔都只是說明什麼樣的情況才能保證函數可重入,但沒有完整定義。按照Wiki上的說法,“A computer program or routine is described as reentrant if it can be safely executed concurrently; that is, the routine can be re-entered while it is already running.”根據筆者的經驗,所謂“重入”,常見的情況是,程序執行到某個函數foo()時,收到信號,於是暫停目前正在執行的函數,轉到信號處理 函數,而這個信號處理函數的執行過程中,又恰恰也會進入到剛剛執行的函數foo(),這樣便發生了所謂的重入。此時如果foo()能夠正確的運行,而且處 理完成後,之前暫停的foo()也能夠正確運行,則說明它是可重入的。

  • 確保可重入:
    要確保函數可重入,需滿足以下幾個條件:
    1、不在函數內部使用靜態或全局數據
    2、不返回靜態或全局數據,所有數據都由函數的調用者提供。
    3、使用本地數據,或者通過製作全局數據的本地拷貝來保護全局數據。
    4、不調用不可重入函數。

  • 不可重入的後果:
    不可重入的後果主要體現在象信號處理函數這樣需要重入的情況中。如果信號處理函數中使用了不可重入的函數,則可能導致程序的錯誤甚至崩潰。


可重入與線程安全
可重入與線程安全並不等同。一般說來,可重入的函數一定是線程安全的,但反過來不一定成立。它們的關係可用下圖來表示:
可重入與線程安全

我們可以採用下面的變化過程來進一步說明上圖:

  • 如果一個函數中用到了全局或靜態變量,那麼它不是線程安全的,也不是可重入的;
  • 如果我們對它加以改進,在訪問全局或靜態變量時使用互斥量或信號量等方式加鎖,則可以使它變成線程安全的,但此時它仍然是不可重入的,因爲通常加鎖方式是針對不同線程的訪問,而對同一線程可能出現問題;
  • 如果將函數中的全局或靜態變量去掉,改成函數參數等其他形式,則有可能使函數變成既線程安全,又可重入。

比如:strtok函數是既不可重入的,也不是線程安全的;加鎖的strtok不是可重入的,但線程安全;而strtok_r既是可重入的,也是線程安全的。

轉載的這篇對可重入函數的定義說得太模糊,下面是鳥人找到的關於”可重入函數“更全面和準確的描述:


轉載的這篇對可重入函數的定義說得太模糊,下面是關於”可重入函數“更全面和準確的描述:

在多線程或有異常控制流的情況下,當某個函數運行到中途時,控制流(也就是當前指令序列)就有可能被打斷而去執行另一個函數.而”另一個函數”很有可能是它本身.,如果在這種情況下不會出現問題,比如說數據或狀態不會被破壞,行爲確定。那麼這個函數就被稱做”可重入”的.
函數是可重入(reentrant)的,是指對於相同的(並且合法的)函數參數(包括無參函數的情況),多次重複調用此函數產生的行爲是可預期的,即函數的行爲一致,或者結果相同。不能保證這一點的函數稱爲不可重入(non-reentrant)函數。

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