目錄數據庫
29.1 類庫和線程安全數組
29.2 基元用戶模式和內核模式構造安全
29.3 用戶模式構造多線程
29.4 內核模式構造併發
線程同步:多個線程同時訪問共享數據時,線程同步能防止數據損壞。函數
繁瑣:在代碼中,必須標識出全部可能由多個線程同時訪問的數據。而後,必須用額外的代碼將這些代碼包圍起來,並獲取和釋放一個線程同步鎖。鎖的做用是確保一次只有一個線程訪問資源,只要有一個代碼塊忘記用鎖包圍,數據就會損壞。鎖:會損壞性能,獲取和釋放鎖是須要時間的,由於要調用一些額外的方法,並且不一樣的CPU必須進行協調,以決定哪一個線程先取得鎖。一次只容許一個線程訪問資源。性能
FCL保證全部靜態方法都是線程安全的。優化
使本身的全部靜態方法都線程安全,使全部實例方法都非線程安全。編碼
基元是指能夠在代碼中使用的最簡單的構造。兩種基元構造:用戶模式和內核模式。spa
用戶模式使用特殊CPU指令來協調線程。意味着協調是在硬件中發生的。也意味着Microsoft Windows操做系統永遠檢測不到一個線程在基元用戶模式的構造上阻塞了。因爲用戶模式的基元構造上阻塞的線程永遠不認爲已阻塞,因此線程池不會建立新線程來替換這種臨時阻塞的線程。
內核模式的構造是由Windows操做系統自身提供的。因此,它們要求在應用程序的線程中調用由操做系統內核實現的函數。將線程從用戶模式切換爲內核模式(或相反)會招致巨大的性能損失。優勢:線程經過內核模式的構造獲取其餘線程擁有的資源時,Windows會阻塞線程以免它浪費CPU時間。當資源變得可用時,Windows會恢復線程,容許它訪問資源。
對於在一個構造上等待的線程 ,若是擁有這個構造的線程一直不釋放它,前者就可能一直阻塞。若是是用戶模式的構造,線程將一直在一個CPU上運行,咱們稱爲「活鎖」。若是是內核模式的構造,線程將一直阻塞,咱們稱爲「死鎖」。理想中的構造:在沒有競爭的狀況下,這個構造應該快並且不會阻塞(用戶模式)。但若是存在對構造的競爭,它被操做系統內核阻塞。——混合構造
CLR的許多線程同步構造實際只是「Win32線程同步構造」的一些面向對象的包裝器。CLR線程就是Windows線程,意味着要由Windows調度線程和控制線程同步。
原子性:一次性。非原子性:一次torn read。
兩種基元用戶模式線程同步構造:
易變構造:在特定的時間,它在包含一個簡單數據類型的變量上執行原子性的讀或寫造做。
互鎖構造:在特定的時間,它在包含一個簡單數據類型的變量上執行原子性的讀或寫操做。
29.3.1 易變構造
高級語言常規構造:if/else,swithc/case,各類循環,局部變量,實參,虛方法調用,操做符重載等。最終,高級語言的編譯器必須將高級構造轉換成低級構造,使計算機能真正作你想作的事情。
C#編譯器將你的C#構造轉換成中間語言(IL)。而後JIT將IL轉換成本機CPU指令,而後由CPU親自處理這些指令。此外,C#編譯器,JIT編譯器,甚至CPU自己均可能優化你的代碼。
Volatile.Writer 方法強迫location中的值在調用時寫入。此外,按照編碼那順序,以前的加載和存儲操做必須在調用Volatile.Write以前發生。
Volatile.Read 方法強迫location中的值在調用時讀取。此外,按照編碼順序,以後的加載和存儲操做必須在調用Volatile.Read以後發生。
當線程經過共享內存相互通訊時,調用Volation.Write來寫入最後一個值,調用Volatile.Read來讀取第一個值。
29.3.2 互鎖構造
Interlocked類中的每一個方法都執行一次原子讀取以及寫入操做。此外,Interlicked的全部方法都創建了完整的內存柵欄。調用某個Interlocked方法以前的任何變量寫入都在這個Interlocked方法調用以前執行;而這個調用以後的任何變量讀取都在這個調用以後讀取。
23.3.3 實現簡單的自旋鎖
若是須要原子性地操做類對象中的一組字段,這種狀況下須要採起一個辦法阻止全部線程,只容許一個進入對字段進行操做的代碼區域。可使用Interlocked的方法構造一個線程同步塊。
23.3.4 Interlocked Anything模式
該模式相似於在修改數據庫記錄時使用的樂觀併發模式。
Windows提供了幾個內核模式的構造來同步線程。內核模式的構造比用戶模式的構造慢得多,一個緣由是它們要求Windows操做系統自身配合,另外一個緣由是在內核對象上調用的每一個方法都形成調用線程從託管代碼轉換爲本機用戶模式代碼,再轉換爲本機內核模式代碼。而後,還要朝相反的方向一路返回。這些在轉換須要大量CPU時間:
優勢:
內核模式的構造檢測到一個資源上的競爭時,Windows會阻塞輸掉的線程,使它不着一個CPU「自旋「,無所謂地浪費處理器資源。
內核模式的構造可實現本機和託管線程相互之間的同步。
內核模式的構造可同步在用一臺機器的不一樣進程中的運行的線程。
內核模式的構造可應用安全性能設置,防止未受權的帳戶訪問它們。
線程可一直阻塞,直到集合中的全部內核模式構造均可用,或直到集合中的任何內核模式構造可用。
在內核模式的構造上阻塞的線程可指定超時值;指定時間內訪問不到但願的資源,線程就能夠解除阻塞並執行其餘任務。
事件和信號時兩種基元內核模式線程同步構造。
WaitHandle基類內部有一個 SafeWaitHandle字段,它容納一個Win32內核對象句柄。這個字段時在構造一個具體WaitHandle派生類時初始化的。除此以外,WaitHandle類公開了由全部派生類繼承的方法。在衣蛾內核模式的構造上調用的每一個方法都表明一個完整的內柵欄。
public abstract class WaitHandle : MarshalByRefObject, IDisposable{ public virtual Boolean WaitOne(); //內部調用 Win32WaitForSingleObjectEx函數 pubilc virtual Boolean WaitOne(Int32 millisecondsTimeOut); public virtual Boolean WaitOne(TimeSpan timeout); public static Boolean WaitAll(WaitHandle[] WaitHandles); //內部調用Win32 WaitForMultipleObjectEx函數 public static Boolean WaitAll(WaitHandle[] WaitHandles, Int32 millisecondsTimeOut); public static Boolean WaitAll(WaitHandle[] WaitHandles, TimeSpan timeout); public static Int32 WaitAny(WaitHandle[] WaitHandles); //內部調用Win32 WaitForMultipleObjectEx函數 public static Int32 WaitAny(WaitHandle[] WaitHandles, Int32 millisecondsTimeOut); public static Int32 WaitAny(WaitHandle[] WaitHandles, TimeSpan timeout); public const Int32 WaitTimeout = 258; //超時就從WaitAny返回這個 public void Dispse(); // 內部調用Win32 CloseHandle函數 - 本身不要調用 }
注意:
能夠調用WaitHandle的WaitOne方法讓調用線程等待底層內核對象受到信號。
能夠調用WaitHandle的靜態WaitAll方法,讓調用線程等待WaitHandle[]中指定的全部內核對象都受到信號。
能夠調用WaitHandle的靜態WaitAny方法,讓調用線程等待WaitHandle[]中指定的全部內核對象都受到信號。
在傳給WaitAny和WaitAll方法的數組中,包含元素不能超過64個,不然方法會拋出一個System.NotSupportedException。
能夠調用WatiHandle的Dispose方法來關閉底層內核對象句柄。
不接受超時參數的那些版本的WaitOne和WaitAll方法應返回void而不是Boolean。
內核模式構造的一個常見用途就是建立在任什麼時候刻只容許它的一個實例運行的應用程序。
每一個進程都有本身的線程,兩個線程都嘗試建立具備相同字符串名稱的一個Seamphore。Windows內核確保只有一個線程實際地建立具備指定名稱的內核對象;建立對象的線程會將它createdNew變量設爲true。對於第二個線程,Windows發現具備指定名稱的內核對象已經存在了。所以,不容許第二個線程建立另外一個同名的內核對象。若是這個線程繼續運行的話,它能訪問和第一個進程的線程所訪問的同樣的內核對象。不一樣進程中的線程就是這樣經過內核對象來通訊的。
29.4.1 Event構造
事件其實只是由內核維護的Boolean變量。事件爲false,在事件上等待的線程就阻塞;事件爲true,就解除阻塞。有兩種事件,即自動重置事件和手動重置事件。當一個自動重置事件爲true時,它只喚醒一個阻塞的線程,由於在解除第一個線程,由於在解除第一線程的阻塞後,內核將事件自動重置回false,形成其他線程繼續阻塞。而當一個手動重置事件爲true時,它解除正在等待它的全部線程的阻塞,由於內核不將事件自動重置回false;
29.4.2 Semaphore構造
信號量(semaphore)其實就是由內核維護的Int32變量。信號量爲0時,在信號量上等待的線程會阻塞;信號量大於0時解除阻塞。在信號量上等待的線程解除阻塞時,內核自動從信號量的計數中減1.信號量海關聯了一個最大Int32值,當前計數決不容許超過最大計數。
多個線程在一個自動重置事件上等待時,設置事件只致使一個線程被解除阻塞。
多個線程在一個手動重置事件上等待時,設置事件致使全部線程被解除阻塞。
多個線程在一個信號量上等待時,釋放信號量致使releaseCount個線程被解除阻塞
29.4.3 Mutex構造
互斥體表明一個互斥的鎖。