線程安全與可重入函數的區別與聯繫

一.對於C語言的函數來說

    在C語言的時代,可重入函數是說當一個函數被不同的線程訪問的時候,每一份調用都獨自使用自己的一份數據,多次調用之間不存在數據共享。所以C語言的可重入函數一定是線程安全的,因爲根本不存在多個線程共享一份數據的問題,也就不會發生訪問衝突。可重入函數的要求是函數不訪問全局或者靜態變量。

    線程安全如果多個線程對同一函數的多次調用之間存在共享數據,所有對共享數據的訪問都必須是序列化的,每個線程按先來後到排隊訪問,也就是不存在同時訪問。

二.對於C++的類來說

    C++中,引入了類的概念,也就是引入了類成員函數的線程安全和線程可重入的概念。

    類的成員函數可重入:類的成員函數在被多個線程在不同實例裏面訪問時,不存在共享數據,也不存在數據衝突。

    類的可重入表示類的所有成員函數都是可重入的。

    類的線程安全:類的同一個實例被線程訪問的時候,對數據的操縱是序列化的,不會發生訪問衝突。

三.線程安全函數

    (1)概念

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

    (2)確保線程安全

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

    (3)線程不安全的後果

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

四.可重入函數

    (1)概念

        常見的情況是,程序執行到某個函數foo()時,收到信號,於是暫停目前正在執行的函數,轉到信號處理函數,而這個信號處理函數的執行過程中,又恰恰也會進入到剛纔執行的函數foo(),這樣便發生了重入。此時如果函數foo()能夠正確的運行,而且處理完成後,之前暫停的foo()也能夠正確運行,則說明它是可重入的。

    (2)確保可重入的幾個條件

        a.不在函數內部使用靜態或者是全局數據;

        b.不返回靜態或者全局數據,所有數據都由函數的調用者提供;

        c.使用本地數據,或者通過製作全局數據的本地拷貝來保護全局數據;

        d.不調用不可重入函數。

    (3)不可重入的後果

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

五.可重入與線程安全

    可重入與線程安全並不等同。一般來說,可重入的函數一定是線程安全的,但反過來不一定成立。下面進行進一步的說明:

    (1)如果一個函數中遇到了靜態或者全局變量,那麼它不是線程安全的,也不是可重入的;

    (2)如果我們對它進行改進,在訪問全局或是靜態變量時使用互斥量或者信號量等方式加鎖,則可以使它變成線程安全的,但此時它仍然是不可重入的,因爲通常加鎖方式是對於不同線程的而訪問,而對同一線程可能會出現問題(可重入函數只和函數訪問的變量類型有關,和是否使用鎖沒有關係;而線程安全和使用鎖的關係密切,很多時候線程安全是靠鎖來保證的);

    (3)如果將函數中的全局或是靜態變量去掉,改成函數參數等其他形式,則有可能使函數變成線程安全的,又是可重入的。


    


 


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