线程安全和可重入函数的联系与区别
?
1、????線程安全:
線程安全是多線程訪問時,采用了加鎖機制,當一個線程訪問該類的某個數據時,進行保護,其他線程不能進行訪問直到該線程訪問完,其他線程才可以使用。不會出現數據不一致或數據污染。
線程不安全就是不提供數據訪問保護,有可能出現多個線程先后更改數據造成所得到的數據是臟數據。
四類線程不安全函數:
(1)不保護共享變量的函數(線程不安全):將這類函數轉化為線程安全的,相對比較容易:可利用像P和V操作這樣的同步操作來保護共享變量。優點在于調用程序中不需要做任何修改,缺點是同步操作將減慢程序的運行時間。
(2)保持跨越多個調用的狀態函數(線程不安全):一個偽隨機數生成器就是這類的不安全函數的簡單例子。
?
usigned int next = 1;
int rand(){
?????? next= next * 1103515245 + 12345;
?????? return(usigned int)(next/65536)%32768;
}
void srand(usigned int seed){
next= seed;
}
rand 函數是線程不安全的,因為當前調用的結果依賴于前次調用的中間結果。當我們調用srand為rand設置一個種子之后,我們反復從一個種子中調用rand,我們能夠預期一個 可重復的隨機數字序列。但如果多個線程同時調用rand函數,那假設就不成立。使用rand函數變為線程安全的唯一方式是重寫它,使得它不再使用任何靜態數據,取而代之的依靠調用者在參數中傳遞狀態信息。缺點要被迫改寫調用程序的代碼。
(3)返回指向靜態變量指針的函數:
某些函數(如gethostbyname)將計算結果放在靜態結構中,并返回一個指向這個結構的指針。如果我們從并發線程中調用這些函數,那么將可能發生災難,因為正在被一個線程使用的結果會被另一個線程悄悄地覆蓋了。
有兩種方法來處理這類線程不安全函數。一種是選擇重寫函數,使得調用者傳遞存放結果的結構地址。這就消除了所有共享數據,但是它要求程序員還要改寫調用者的代碼。
如果線程不安全函數是難以修改或不可修改的(例如,它是從一個庫中鏈接過來的),那么另外一種選擇就是使用lock-and-copy(加鎖-拷貝)技術。這個概念將線程不安全函數與互斥鎖聯系起來。在每個調用位置,對互斥鎖加鎖,調用函數不安全函數,動態地為結果非配存儲器,拷貝函數返回的結果到這個存儲器位置,然后對互斥鎖解鎖。一個吸引人的變化是定義了一個線程安全的封裝(wrapper)函數,它執行lock-and-copy,然后調用這個封轉函數來取代所有線程不安全的函數。例如下面的gethostbyname的線程安全函數。
struct hostent* gethostbyname_ts(char* host){
struct hostent* shared,* unsharedp;
??? unsharedp =Malloc(sizeof(struct hostent));
P(&mutex)
Hared =gethostbyname(hostname);*unsharedp =* shared;
V(&mutex);returnunsharedp;
}
?
(4)調用線程不安全函數的函數
如果函數f調用線程不安全函數g,那么f就是線程不安全的嗎?不一定。如果g是類2類函數,即依賴于跨越多次調用的狀態,那么f也是不安全的,而且除了重寫g以外,沒有什么辦法。然而如果g是第1類或者第3類函數,那么只要用互斥鎖保護調用位置和任何得到的共享數據,f可能仍然是線程安全的。比如上面的gethostbyname_ts。
?
?
?
2、????可重入函數:
函數被不同的控制流程調?用,有可能在第一次調用還沒返回時就再次進入該函 數,這稱為重入,insert函數訪問一個全局鏈表,有可能因為重入而造成錯亂,像這樣的函數稱為 不可重入函數,反之,如果一個函數只訪問自己的局部變量或參數,則稱為可重入(Reentrant)函數。
?
?可重入特點
由于可重入函數多次調用不會出錯,因此可重入函數不用擔心數據會被破壞。可重入函數任何時候都可以被中斷,一段時間后又可以運行,而相應的數據不會丟失。可重入函數只使用局部變量,即保存在CPU寄存器或者堆棧中;或者如果使用全局變量時,則要對全局變量予以保護。
?
?不可重入特點
如果一個函數符合以下條件之一的,則是不可重入的:
(1)調用了malloc/free函數,因為malloc函數是用全局鏈表來管理堆的。
(2)調用了標準I/O庫函數,標準I/O庫的很多實現都以不可重入的方式使用全局數據結構。
(3)可重入體內使用了靜態的數據結構。
?
3、????可重入函數與線程安全的關系:
可重入函數與線程安全的區別與聯系:
(1)線程安全是在多個線程情況下引發的,而可重入函數可以在只有一個線程的情況下來說。
(2)線程安全不一定是可重入的,而可重入函數則一定是線程安全的。
(3)如果一個函數中有全局或靜態變量,那么這個函數既不是線程安全也不是可重入的。如果我們對它加以改進,在訪問全局或靜態變量時使用互斥量或信號量等方式加鎖,則可以使它變成線程安全的,但此時它仍然是不可重入的,如果將函數中的全局或靜態變量去掉,改成函數參數等其他形式,則有可能使函數變成既線程安全,又可重入。
(4)如果將對臨界資源的訪問加上鎖,則這個函數是線程安全的,但如果這個重入函數若鎖還未釋放則會產生死鎖,因此是不可重入的。
(5)線程安全函數能夠使不同的線程訪問同一塊地址空間,而可重入函數要求不同的執行流對數據的操作互不影響使結果是相同的。
(6)strtok函數是既不可重入的,也不是線程安全的;加鎖的strtok不是可重入的,但線程安全;而strtok_r既是可重入的,也是線程安全的。
?
注:
1.可重入概念只和函數訪問的變量類型有關,和是否使用鎖沒有關系。
2.而線程安全和鎖的使用關系密切,很多時候線程安全是靠鎖來保證的
?
?
總結
以上是生活随笔為你收集整理的线程安全和可重入函数的联系与区别的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Unity轻松制作俯视角游戏插件TopD
- 下一篇: 3.ZooKeeper客户端Curato