1. 程式人生 > >Linux可重入函式和執行緒安全的區別與聯絡(轉)

Linux可重入函式和執行緒安全的區別與聯絡(轉)

*****可重入函式

      函式被不同的控制流程呼叫,有可能在第一次呼叫還沒返回時就再次進入該函式,這稱為重入。

      當程式執行到某一個函式的時候,可能因為硬體中斷或者異常而使得在使用者正在執行的程式碼暫時終端轉而進入你核心,這個時候如有一個訊號需要被處理,而處理的這個訊號的時候又會重新呼叫剛才中斷的函式,如果函式內部有一個全域性變數需要被操作,那麼,當訊號處理完成之後重新返回使用者態恢復中斷函式的上下文再次繼續執行的時候,對同一個全域性變數的操作結果可能就會發生改變而並不如我們預期的那樣,這樣的函式被稱為不可重入函式。例如在進行連結串列的插入時,插入函式訪問一個全域性連結串列,有可能因為重入而造成錯亂。

      相對應的,當一個執行流因為異常或者被核心切換而中斷正在執行的函式而轉為另外一個執行流時,當後者的執行流對同一個函式的操作並不影響前一個執行流恢復後執行函式產生的結果,我們就稱這個函式為可重入函式。可重入函式只訪問自己的區域性變數或引數。其實總結就是:一個可重入函式可以被多個執行流重複進入,內部使用的資料都應該來自於自身的棧空間,包括返回值也不應該是全域性或者靜態的,可以允許有該函式的多個副本在執行,而正是因為其中的操作資料都來自於自身的棧空間,而每次呼叫函式會開闢不同的棧空間,因此二者互不影響。

下面為一個不可重入函式的例子:



為了防止這種情況的發生,可以將g_val改為區域性變數。這樣函式兩次被呼叫的時候,兩次結果最終並不影響且相等,函式變為可重入函數了。

可重入函式的分類

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

一個可重入函式需要滿足的是:
1、不使用全域性變數或靜態變數;
2、不使用用malloc或者new開闢出的空間;
3、不呼叫不可重入函式;
4、不返回靜態或全域性資料,所有資料都有函式的呼叫者提供;
5、使用本地資料,或者通過製作全域性資料的本地拷貝來保護全域性資料;

不可重入函式符合以下條件之一:
1、呼叫了malloc/free函式,因為malloc函式是用全域性連結串列來管理堆的。
2、呼叫了標準I/O庫函式,標準I/O庫的很多實現都以不可重入的方式使用全域性資料結構。
3、可重入體內使用了靜態的資料結構。

*****執行緒安全的概念

       一個函式被稱為執行緒安全的(thread-safe),當且僅當被多個併發程序反覆呼叫時,它會一直產生正確的結果。反之,如果一個函式不是執行緒安全的,我們就說它是執行緒不安全的(thread-unsafe)。所以,有這麼四類函式稱為執行緒不安全的:

1、不保護共享變數的函式;
2、函式狀態隨著呼叫改變的函式;
3、返回指向靜態變數指標的函式;
4、呼叫執行緒不安全函式的函式;
      如果你的程式碼所在的程序中有多個執行緒在同時執行,而這些執行緒可能會同時執行這段程式碼。如果每次執行結果和執行的結果是一樣的,而且其他的變數的值也和預期的是一樣的,就是執行緒安全的。或者說一個類或者程式所提供的介面對於執行緒來說是原子操作或者多個執行緒之間的切換不會導致該介面的執行結果存在二義性,也就是說我們不用考慮同步的問題。
     執行緒安全問題都是由全域性變數及靜態變數引起的。若每個執行緒中對全域性變數、靜態變數只有讀操作,而無寫操作,一般來說,這個全域性變數是執行緒安全的;若有多個執行緒同時執行寫操作,一般都需要考慮執行緒同步,否則的話就可能影響執行緒安全。一般來說,一個函式被稱為執行緒安全的,當且僅當被多個併發執行緒反覆呼叫時,它會一直產生正確的結果。

      根據執行緒的同步與互斥,也就是當兩個執行緒同時訪問到同一個臨界資源的時候,如果對臨界資源的操作不是原子的就會產生衝突,使得結果並不如最終預期的那樣。例如以下程式:


發現上述結果中所得結果不是1000,並且多次執行結果都不同,因此此程式執行緒是不安全的。
因此,執行緒安全是指當多個執行緒訪問同一個區域的時候其最終的結果是可預期的,並不會因為產生衝突或者異常中斷再次恢復而使結果不可預期。

*****可重入函式與執行緒安全的區別與聯絡

函式可以是可重入的,也可以是執行緒安全的,或者兩者皆是,或者兩者皆非。不可重入函式不能由多個執行緒使用。

1、執行緒安全是在多執行緒情況下引發的,而可重入函式可以在只有一個執行緒的情況下發生。

2、執行緒安全不一定是可重入的,而可重入函式則一定是執行緒安全的。

3、如果一個函式有全域性變數,則這個函式既不是執行緒安全也不是可重入的。

4、如果一個函式當中的資料全身自身棧空間的,則這個函式即使執行緒安全也是可重入的。

5、如果將對臨界資源的訪問加鎖,則這個函式是執行緒安全的;但如果重入函式的話加鎖還未釋放,則會產生死鎖,因此不能重入。

6、執行緒安全函式能夠使不同的執行緒訪問同一塊地址空間,而可重入函式要求不同的執行流對資料的操作不影響結果,使結果是相同的。
---------------------
原文:https://blog.csdn.net/scenlyf/article/details/52074444