可重入函數和線程安全

一、可重入函數

    可重入函數主要用於多任務環境中,一個可重入的函數簡單來說就是可以被中斷的函數,也就是說,可以在這個函數執行的任何時刻中斷它,轉入OS調度下去執行另外一段代碼,而返回控制時不會出現什麼錯誤;,局部變量可重入函數; 不可重入的函數由於使用了一些系統資源,比如全局變量區,中斷向量表等,所以它如果被中斷的話,可能會出現問題,這類函數是不能運行在多任務環境下的。

                    wKiom1cxR6WjWsAdAADbeVhDlIk346.jpg如上圖:

    main函數調用insert函數向一個鏈表head中插入節點node1,插入操作分爲兩步,剛做完第一步的 時候,因爲硬件中斷使進程切換到內核,再次回用戶態之前檢查到有信號待處理,於是切換到sighandler函數,sighandler也調用insert函數向同一個鏈表head中插入節點node2,插入操作的兩步都做完之後從ighandler返回內核態,再次回到用戶態就從main函數調用的insert函數中繼續往下執行,先前做第一步之後被打斷,現在繼續做完第二步。結果是,main函數和sighandler先後向鏈表中插入兩個節點,而最後只有一個節點真正插入鏈表中了。這就出現了泄漏。像上例這樣,insert函數被不同的控制流程調用,有可能在第一次調用還沒返回時就再次進入該函數,這稱爲重入,insert函數訪問一個全局鏈表,有可能因爲重入而造成錯亂,像這樣的函數稱爲不可重入函數,反之,如果一個函數只訪問自己的局部變量或參數,則稱爲可重入(Reentrant) 函數

   1、 如果一個函數符合以下條件之一則是不可重入的

    ①、調用了malloc或free,因爲malloc也是用全局鏈表來管理堆的。

    ②、調用了標準I/O庫函數。標準I/O庫的很多實現都以不可重入的方式使用全局數據結構。

二、線程安全

  如果代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。如果每次運行結果和單線程運行的結果是一樣的,而且其他的變量的值也和預期的是一樣的,就是線程安全的。或者說:一個類或者程序所提供的接口對於線程來說是原子操作或者多個線程之間的切換不會導致該接口的執行結果存在二義性,       

線程安全問題都是由全局變量及靜態變量引起的。若每個線程中對全局變量、靜態變量只有讀操作,而無寫操作,一般來說,這個全局變量是線程安全的;若有多個線程同時執行寫操作,一般都需要考慮線程同步,否則的話就可能影響線程安全。產生線程安全的本質在於:線程共享和執行流發生了干擾。要確保線程安全,線程在對共享變量進行訪問時必須以加鎖的方式。

      比如一個 ArrayList 類,在添加一個元素的時候,它可能會有兩步來完成:1. 在 Items[Size] 的位置存放此元素;2. 增大 Size 的值。

            在單線程運行的情況下,如果 Size = 0,添加一個元素後,此元素在位置 0,而且 Size=1;而如果是在多線程情況下,比如有兩個線程,線程 A 先將元素存放在位置 0。但是此時 CPU 調度線程A暫停,線程 B 得到運行的機會。線程B也向此 ArrayList 添加元素,因爲此時 Size 仍然等於 0 (注意,我們假設的是添加一個元素是要兩個步驟哦,而線程A僅僅完成了步驟1),所以線程B也將元素存放在位置0。然後線程A和線程B都繼續運行,都增加 Size 的值。那好,我們來看看 ArrayList 的情況,元素實際上只有一個,存放在位置 0,而 Size 卻等於 2。這就是“線程不安全”了。


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

    1、可重入函數一定是線程安全的,單線程安全不一定是可重入的

                    

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