1、執行緒安全
(1)概念:
執行緒安全的概念比較直觀。一般說來,乙個函式被稱為執行緒安全的,當且僅當被多個併發執行緒反覆呼叫時,它會一
直產生正確的結果。
如果你的程式所在的程序中有
多個執行緒在同時執行
,而這些執行緒可能
同時執行一段**或同時訪
問乙個物件
,如果每次執行完這段**或訪問完這個物件之後,
所得到的結果和單執行緒執行的結果一樣
,而其他變數
的值也和預期的保持一致,那麼就認為是執行緒安全的。
也就是說
當多個執行緒同時執行同一段**,不會造成資源的
衝突,不會產生錯誤的結果就是執行緒安全的
。如果有一段執行緒安全的**(原子操作或執行緒間切換不會導致結果的二
義性),
它在多個執行緒中使用是不需要作同步處理的
。執行緒不安全的後果:
執行緒不安全可能導致的後果是顯而易見的共享變數的值由於不同執行緒的訪問,可能發生不可預料的變化,進而導致
程式的錯誤,甚至崩潰,
執行緒不安全
的**在多執行緒環境中必須作
同步處理。
我們定義四類(有相交的)執行緒不安全函式。
1)不保護共享變數的函式
將這類執行緒不安全函式變為執行緒安全的,相對比較容易:利用像p和v操作這樣的同步操作來保護共享變數。這個
方法的優點是在呼叫程式中不需要做任何修改,缺點是同步操作將減慢程式的執行時間。
2)保持跨越多個呼叫的狀態函式
3)返回指向靜態變數指標的函式
某些函式(如gethostbyname)將計算結果放在靜態結構中,並返回乙個指向這個結構的指標。如果我們從併發
執行緒中呼叫這些函式,那麼將可能發生災難,因為正在被乙個執行緒使用的結果會被另乙個執行緒悄悄地覆蓋了。
有兩種方法來處理這類執行緒不安全函式
。一種是選擇
重寫函式
,使得呼叫者傳遞存放結果的結構位址。這就消除了所
有共享資料,但是它要求程式設計師還要改寫呼叫者的**。如果執行緒不安全函式是難以修改或不可修改的(例如,它是
從乙個庫中鏈結過來的),那麼另外一種選擇就是
使用lock-and-copy(加鎖-拷貝)技術
。這個概念將執行緒不安全函
數與互斥鎖聯絡起來。在每個呼叫位置,對互斥鎖加鎖,呼叫函式不安全函式,動態地為結果非配儲存器,拷貝函式
數,它執行lock-and-copy,然後呼叫這個封轉函式來取代所有執行緒不安全的函式。
4)呼叫執行緒不安全函式的函式
如果函式f呼叫執行緒不安全函式g,那麼f就是執行緒不安全的嗎?不一定。如果g是類2類函式,即依賴於跨越多次調
用的狀態,那麼f也是不安全的,而且除了重寫g以外,沒有什麼辦法。然而如果g是第1類或者第3類函式,那麼只要
用互斥鎖保護呼叫位置和任何得到的共享資料,f可能仍然是執行緒安全的。
乙個類或者程式所提供的介面對於執行緒來說是原子操作或者多個執行緒之間的切換不會導致該介面的執行結果存在
二義性,也就是說我們不用考慮同步的問題。
執行緒安全問題都是由全域性變數及靜態變數引起的。若每個執行緒中對全域性變數、靜態變數只有讀操作,而無寫操
作,一般來說,這個全域性變數是執行緒安全的;若有多個執行緒同時執行寫操作,一般都需要考慮執行緒同步,否則的話就
可能影響執行緒安全。一般來說,乙個函式被稱為執行緒安全的,當且僅當被多個併發執行緒反覆呼叫時,它會一直產生正
確的結果。
根據執行緒的同步與互斥,也就是當兩個執行緒同時訪問到同乙個臨界資源的時候,如果
對臨界資源的操作不是原子
的就會產生衝突,使得結果並不如最終預期的那樣。
(2)如何避免執行緒安全?
要避免執行緒安全,主要需要考慮的是執行緒之間的共享變數。屬於同一程序的不同執行緒會共享程序記憶體空間中的全
局區和堆,而私有的執行緒空間則主要包括棧和暫存器。因此,對於同一程序的不同執行緒來說,每個執行緒的區域性變數都
是私有的,而全域性變數、區域性靜態變數、分配於堆的變數都是共享的。在對這些共享變數進行訪問時,如果要保證線
程安全,則必須通過加鎖的方式。
2、可重入函式
(1)概念:
在多執行緒或有異常控制流的情況下,當某個函式執行到中途時,控制流(也就是當前指令序列)就有可能被打斷而去執
行另乙個函式.而"另乙個函式"很有可能是它本身.,如果在這種情況下不會出現問題,比如說資料或狀態不會被破壞,
行為確定。那麼這個函式就被稱做"可重入"的.函式是
可重入(reentrant)的,是指對於相同的(並且合法的)函式
引數(包括無參函式的情況),多次重複呼叫此函式產生的行為是可預期的,即函式的行為一致,或者結果相同。不
能保證這一點的函式稱為
不可重入
(non-reentrant)函式。簡單的描述為:
可重入函式
1)不在函式內部使用靜態或全域性資料
2)不返回指向靜態資料的指標,所有資料都由函式呼叫者來提供
3)使用本地資料,或通過製作全域性資料的本地拷貝來保護全域性資料
4)如果必須訪問全域性變數,利用互斥機制來保護全域性變數
5)不呼叫不可重入函式
不可重入函式
1)函式中使用了靜態變數,不論全域性或者區域性靜態變數
2)函式返回靜態變數或靜態的資料結構
3)函式體內呼叫了malloc()或者free()函式
4)函式中呼叫了不可重入函式
(2)可重入的判斷條件:
要確保函式可重入,需滿足一下幾個條件:
1)不在函式內部使用靜態或全域性資料
2)不返回靜態或全域性資料,所有資料都由函式的呼叫者提供。
3)使用本地資料,或者通過製作全域性資料的本地拷貝來保護全域性資料。
4)不呼叫不可重入函式。
(3)可重入函式的分類
1)顯式可重入函式
如果所有函式的引數都是傳值傳遞的(沒有指標),並且所有的資料引用都是本地的自動棧變數(也就是說沒有
引用靜態或全域性變數),那麼函式就是顯示可重入的,也就是說不管如何呼叫,我們都可斷言它是可重入的。
2)隱式可重入函式
可重入函式中的一些引數是引用傳遞(使用了指標),也就是說,在呼叫執行緒小心地傳遞指向非共享資料的指標
時,它才是可重入的。 可重入函式可以有多餘乙個任務併發使用,而不必擔心資料錯誤,相反,不可重入函式不
能由超過乙個任務所共享,除非能確保函式的互斥(或者使用訊號量,或者在 **的關鍵部分禁用中斷)。可重入函
數可以在任意時刻被中斷,稍後再繼續執行,不會丟失資料,可重入函式要麼使用本地變數,要麼在使用全域性變數時
保護自己 的資料。
(4)乙個可重入函式需要滿足的是:
1)不使用全域性變數或靜態變數;
2)不使用用malloc或者new開闢出的空間;
3)不呼叫不可重入函式;
4)不返回靜態或全域性資料,所有資料都有函式的呼叫者提供;
5)使用本地資料,或者通過製作全域性資料的本地拷貝來保護全域性資料;
(5)不可重入特點
如果乙個函式符合以下條件之一的,則是不可重入的:
1)呼叫了malloc/free函式,因為malloc函式是用全域性鍊錶來管理堆的。
2)呼叫了標準i/o庫函式,標準i/o庫的很多實現都以不可重入的方式使用全域性
資料結構
。3)可重入體內使用了靜態的資料結構。
可重入函式是執行緒安全函式的一種,其特點在於它們被多個執行緒呼叫時,不會引用任何共享資料,也就是不引用
靜態或全域性變數。
可重入函式通常要比不可重入的執行緒安全函式效率高一些,因為它們不需要同步操作。更進一步
說,將第2類執行緒不安全函式轉化為執行緒安全函式的唯一方法就是重寫它,使之可重入。函式可以是可重入的,也可
以是執行緒安全的,或者兩者皆是,或者兩者皆非。
不可重入函式不能由多個執行緒使用。
1)可重入函式一定是執行緒安全函式
2)執行緒安全函式不一定是可重入函式(有可能通過互斥機制實現執行緒安全不安全函式—>執行緒安全函式的轉換
等)3)可重入性要強於執行緒安全性(相當於函式產生相同結果時可重入性的條件要苛刻)
4)執行緒安全是在多執行緒情況下引發的,而可重入函式可以在只有乙個執行緒的情況下發生。
5)如果乙個函式有全域性變數,則這個函式既不是執行緒安全也不是可重入的。
6)如果乙個函式當中的資料全身自身棧空間的,則這個函式即使執行緒安全也是可重入的。
7)如果將對臨界資源的訪問加鎖,則這個函式是執行緒安全的;但如果重入函式的話加鎖還未釋放,則會產生死鎖,
因此不能重入。
8)執行緒安全函式能夠使不同的執行緒訪問同一塊位址空間,而可重入函式要求不同的執行流對資料的操作不影響結
果,使結果是相同的。
對比執行緒安全和可重入函式
一 執行緒安全函式 1 什麼是執行緒安全 乙個函式被稱為執行緒安全,當且僅當它被多個併發的程序反覆呼叫時,它會一直產生正確的結果。反之,如果乙個函式不是執行緒安全的,我們就稱它是不安全的。執行緒安全主 要是針對資料競爭來說的,就是說,如果資料不需要共享,那就讓每個資料私有,如果需要共享,就得加鎖。2...
對比執行緒安全和可重入函式
函式被不同的控制流程呼叫,有可能在第一次呼叫還沒返回時就再次 進入該函式,這稱為重入。然而有可能因為重入而造成錯亂,像這樣 的函式稱為不可重入函式,反之,如果乙個函式只訪問自己的區域性變數或引數,則稱為可重入 reentrant 函式。可重入函式 重入即表示重複進入,首先它意味著這個函式可以被中斷,...
執行緒安全和可重入函式
執行緒安全 當多個併發執行緒執行同乙個函式,我們都能得到正確的返回值。當多個執行緒併發的呼叫乙個函式。如果對全域性資料或者靜態資料在不加任何鎖以及安全性的處理情況下,就會對多次修改資料的錯誤。比如我正在願意個執行緒裡處理乙個全域性變數的 1 正減完。結果還沒有返回,就被另乙個執行緒切出去了,而那個執...