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


1、線程安全
線程安全:若是一個函數在同一時刻能夠被多個線程安全的調用,就稱該函數是線程安全的。

不須要共享時,請爲每一個線程提供一個專用的數據副本。若是共享很是重要,則提供顯式同步,以確保程序以肯定的方式操做。經過將過程包含在語句中來鎖定和解除鎖定互斥,可使不安全過程變成線程安全過程,並且能夠進行串行化。

不少函數並非線程安全的,由於他們返回的數據是存放在靜態的內存緩衝區中的。經過修改接口,由調用者自行提供緩衝區就可使這些函數變爲線程安全的。
操做系統實現支持線程安全函數的時候,會對POSIX.1中的一些非線程安全的函數提供一些可替換的線程安全版本。
例如,gethostbyname()是線程不安全的,在Linux中提供了gethostbyname_r()的線程安全實現。
函數名字後面加上"_r",以代表這個版本是可重入的(對於線程可重入,也就是說是線程安全的,但並非說對於信號處理函數也是可重入的,或者是異步信號安全的)。

多線程程序中常見的疏忽性問題
1> 將指針做爲新線程的參數傳遞給調用方棧。
2> 在沒有同步機制保護的狀況下訪問全局內存的共享可更改狀態。
3> 兩個線程嘗試輪流獲取對同一對全局資源的權限時致使死鎖。其中一個線程控制第一種資源,另外一個線程控制第二種資源。其中一個線程放棄以前,任何一個線程都沒法繼續
操做。
4> 嘗試從新獲取已持有的鎖(遞歸死鎖)。
5> 在同步保護中建立隱藏的間隔。若是受保護的代碼段包含的函數釋放了同步機制,而又在返回調用方以前從新獲取了該同步機制,則將在保護中出現此間隔。結果具備誤導性。對於調用方,表面上看全局數據已受到保護,而實際上未受到保護。
6> 將UNIX 信號與線程混合時,使用sigwait(2) 模型來處理異步信號。
7> 調用setjmp(3C) 和longjmp(3C),而後長時間跳躍,而不釋放互斥鎖。
8> 從對*_cond_wait() 或*_cond_timedwait() 的調用中返回後沒法從新評估條件。安全

2、可重入函數數據結構

重入即表示重複進入,首先它意味着這個函數能夠被中斷,其次意味着它除了使用本身棧上的變量之外不依賴於任何環境(包括static),這樣的函數就是purecode(純代碼)可重入,能夠容許有該函數的多個副本在運行,因爲它們使用的是分離的棧,因此不會互相干擾。
可重入函數是線程安全函數,可是反過來,線程安全函數未必是可重入函數。
信號就像硬件中斷同樣,會打斷正在執行的指令序列。信號處理函數沒法判斷捕獲到信號的時候,進程在何處運行。若是信號處理函數中的操做與打斷的函數的操做相同,並且這個操做中有靜態數據結構等,當信號處理函數返回的時候(固然這裏討論的是信號處理函數能夠返回),恢復原先的執行序列,可能會致使信號處理函數 中的操做覆蓋了以前正常操做中的數據。
不可重入函數的緣由在於:
1> 已知它們使用靜態數據結構
2> 它們調用malloc和free.
由於malloc一般會爲所分配的存儲區維護一個連接表,而插入執行信號處理函數的時候,進程可能正在修改此連接表。
3> 它們是標準IO函數.
由於標準IO庫的不少實現都使用了全局數據結構即便對於可重入函數,在信號處理函數中使用也須要注意一個問題就是errno。一個線程中只有一個errno變量,信號處理函數中使用的可重入函數也有可能 會修改errno。例如,read函數是可重入的,可是它也有可能會修改errno。所以,正確的作法是在信號處理函數開始,先保存errno;在信號處 理函數退出的時候,再恢復errno。

例如,程序正在調用printf輸出,可是在調用printf時,出現了信號,對應的信號處理函數也有printf語句,就會致使兩個printf的輸出混雜在一塊兒。
若是是給printf加鎖的話,一樣是上面的狀況就會致使死鎖。對於這種狀況,採用的方法通常是在特定的區域屏蔽必定的信號。
屏蔽信號的方法:
1> signal(SIGPIPE, SIG_IGN); //忽略一些信號
2> sigprocmask()
sigprocmask只爲單線程定義的
3> pthread_sigmask()
pthread_sigmasks能夠在多線程中使用多線程

 

 

3、總結
一、判斷一個函數是否是可重入函數,在於判斷其可否能夠被打斷,打斷後恢復運行可以獲得正確的結果。(打斷執行的指令序列並不改變函數的數據)
判斷一個函數是否是線程安全的,在於判斷其可否在多個線程同時執行其指令序列的時候,保證每一個線程都可以獲得正確的結果。

二、若是一個函數對多個線程來講是可重入的,則說這個函數是線程安全的,但這並不能說明對信號處理程序來講該函數也是可重入的。
三、若是函數對異步信號處理程序的重入是安全的,那麼就能夠說函數是"異步-信號安全"的。異步

四、可重入概念只和函數訪問的變量類型有關,和是否使用鎖沒有關係!而線程安全和鎖的使用關係密切,不少時候線程安全是靠鎖來保證的。ide

相關文章
相關標籤/搜索