14.线程安全?线程不安全?可重入函数?不可重入函数?
線程安全問題
基本定義
線程安全:簡單來說線程安全就是多個線程并發執行同一段代碼時,不會出現不同的結果,我們就可以說該線程是安全的;
線程不安全:如果多線程并發執行時會產生不同的結果,則該線程就是不安全的。
線程安全產生的原因:大多是因為對全局變量和靜態變量的操作。
常見的線程不安全的函數:
(1)不保護共享變量的函數
(2)函數狀態隨著被調用,狀態發生變化的函數
(3)返回指向靜態變量指針的函數
(4)調用線程不安全函數的函數
常見的線程安全的情況
(1)每個線程對全局變量或者靜態變量只有讀取的權限,而沒有寫入的權限,一般來說這些線程是安全的;
(2)類或者接口對于線程來說都是原子操作;
(3)多個線程之間的切換不會導致該接口的執行結果存在二義性;
?
代碼演示
#include<stdio.h> #include<pthread.h>int value=0;void* func(void* arg){int i=0;while(i<10000){int tmp=value;value=i;printf("value is %d\n",value);value=tmp+1;i++;} }int main() {pthread_t id1,id2;pthread_create(&id1,NULL,func,NULL);pthread_create(&id2,NULL,func,NULL);pthread_join(id1,NULL);pthread_join(id2,NULL);printf("value is %d\n",value);return 0; }?
運行結果(可見在存在線程安全時得到的結果并不是我們所期待的):
可重入函數
基本定義
重入:同一個函數被不同的執行流調用,當前一個流程還沒有執行完,就有其他的進程已經再次調用(執行流之間的相互嵌套執行);
可重入:多個執行流反復執行一個代碼,其結果不會發生改變,通常訪問的都是各自的私有棧資源;
不可重入:多個執行流反復執行一段代碼時,其結果會發生改變;
可重入函數:當一個執行流因為異常或者被內核切換而中斷正在執行的函數而轉為另外一個執行流時,當后者的執行流對同一個函數的操作并不影響前一個執行流恢復后執行函數產生的結果;
不可重入函數:當程序運行到某一個函數的時候,可能因為硬件中斷或者異常而使得在用戶正在執行的代碼暫時終端轉而進入你內核,這個時候如有一個信號需要被處理,而處理的這個信號的時候又會重新調用剛才中斷的函數,如果函數內部有一個全局變量需要被操作,那么,當信號處理完成之后重新返回用戶態恢復中斷函數的上下文再次繼續執行的時候,對同一個全局變量的操作結果可能就會發生改變而并不如我們預期的那樣,這樣的函數被稱為不可重入函數。例如在進行鏈表的插入時,插入函數訪問一個全局鏈表,有可能因為重入而造成錯亂。
可重入函數滿足條件
(1)不使用全局變量或靜態變量;
(2)不使用用malloc或者new開辟出的空間;
(3)不調用不可重入函數;
(4)不返回靜態或全局數據,所有數據都有函數的調用者提供;
(5)使用本地數據,或者通過制作全局數據的本地拷貝來保護全局數據;
不可重入函數符合以下條件之一
(1)調用了malloc/free函數,因為malloc函數是用全局鏈表來管理堆的。
(2)調用了標準I/O庫函數,標準I/O庫的很多實現都以不可重入的方式使用全局數據結構。
(3)可重入體內使用了靜態的數據結構。
?
可重入函數分類
(1)顯式可重入函數
如果所有函數的參數都是傳值傳遞的(沒有指針),并且所有的數據引用都是本地的自動棧變量(也就是說沒有引用靜態或全局變量),那么函數就是顯示可重入的,也就是說不管如何調用,我們都可斷言它是可重入的。
(2)隱式可重入函數
可重入函數中的一些參數是引用傳遞(使用了指針),也就是說,在調用線程小心地傳遞指向非共享數據的指針時,它才是可重入的。
可重入函數可以有多余一個任務并發使用,而不必擔心數據錯誤,相反,不可重入函數不能由超過一個任務所共享,除非能確保函數的互斥(或者使用信號量,或者在 代碼的關鍵部分禁用中斷)。可重入函數可以在任意時刻被中斷,稍后再繼續運行,不會丟失數據,可重入函數要么使用本地變量,要么在使用全局變量時保護自己 的數據。
?
代碼演示:
#include<stdio.h> #include<signal.h>int value=0;void fun(){int i=0;while(i++<5){value++;printf("value is %d\n",value);sleep(1);} }int main() {signal(2,fun);fun();printf("the value is %d\n",value);return 0; }
運行結果對比:
?
可重入函數與線程安全的區別與聯系
聯系:
函數可以是可重入的,是線程安全的,或者二者皆是,或者二者皆非。不可重入的函數不能由多個線程使用。另外,或許不可能讓某個不可重入的函數是線程安全的。
區別:
(1)可重入函數是線程安全函數的一種,其特點在于它們被多個線程調用時,不會引用任何共享數據。
(2)線程安全是在多個線程情況下引發的,而可重入函數可以在只有一個線程的情況下來說。
(3)線程安全不一定是可重入的,而可重入函數則一定是線程安全的。
(4)如果一個函數中有全局變量,那么這個函數既不是線程安全也不是可重入的。
(5)如果將對臨界資源的訪問加上鎖,則這個函數是線程安全的,但如果這個重入函數若鎖還未釋放則會產生死鎖,因此是不可重入的。
(6)線程安全函數能夠使多個不同線程訪問同一塊地址空間,而可重入函數要求不同的執行流對數據的操作互不影響使結果是相同的。
總結
以上是生活随笔為你收集整理的14.线程安全?线程不安全?可重入函数?不可重入函数?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 13.常用Linux命令
- 下一篇: 剑指offer:约瑟夫环的问题