1. 程式人生 > >多執行緒和鎖和原子操作和記憶體柵欄(一)

多執行緒和鎖和原子操作和記憶體柵欄(一)

執行緒的定義是執行流的最小單元,而程序是一個邏輯執行緒容器,用來隔離執行緒。

Task類封裝了執行緒池執行緒,啟動的所有線都由執行緒池管理,他提供了很多使用方便的API函式,使多執行緒開發變得容易。


上述程式碼中我啟動了一個執行緒,並在執行緒方法中使用了非同步關鍵字,非同步方法實現了一個狀態機,能夠在等待任務完成時繼續執行之後的程式碼,這裡利用該特性實現執行緒順序執行,

並且上述程式碼使用了ContinueWith方法,這是一個任務執行完成時的延續方法,引數x代表了完成的任務,Delay方法是延遲方法,

狀態機其實是編譯器生成的一個結構體,內部使用了goto 關鍵字,把非同步方法封裝在狀態機中,由狀態機去執行非同步方法,await前後的執行緒上下文相同,在GUI程式中可以把耗時的方法通過 await + Task 的方式讓其他執行緒來執行,而且並不會阻塞UI執行緒。await前後的上下文是相同的,但如果不想相同可以使用ConfigureAwait(false),如果在GUI程式中,這會導致異常,因為介面的元素只能由UI執行緒修改,同時這也可以避免死鎖。


有時候在客戶端我們的程式碼需要被C++函式回掉時或者任何我們都不能改變別人使用多執行緒來執行我們只能用UI執行緒才能執行的程式碼時,我們需要強制同步UI執行緒上下文使得程式碼正常執行,不是this.Dispacher方法而是使用TaskScheduler類來提前獲取UI執行緒上下文,並開啟任務,讓任務在指定上下文中執行,


如果希望使用一個任務又希望能夠順序、同步執行,可以使用GetAwaiter().GetResult()組合呼叫,或者使用Wait()方法,但更建議使用前面一種


如果任務擁有返回值可以直接使用Result屬性


並不建議使用Wait()  Result,有時候這會造成死鎖。


記憶體柵欄:變數在進入記憶體柵欄並讀取時,變數的值已經寫入完畢,離開柵欄時,變數的值被讀取時也已經寫入完畢,只有一個執行緒能夠進入記憶體柵欄,這保證了多執行緒的資料同步。


在上面這個例子中,我啟動了三個執行緒。並對變數target遞增,結果理應是30000,而輸出結果卻不是,這是因為在多執行緒場景下,可能會在某一個時刻幾個執行緒同時讀取到一個值然後遞增,並重新賦值,導致變數資料被同樣的值覆蓋。


而使用了記憶體柵欄則能很好的解決問題,記憶體柵欄保證在同一時刻,只有一個執行緒可以進入,


當然使用volatile關鍵字也是一樣的。


原子操作是一種一次性的操作,不會出現中間變化,並且是執行緒安全、執行緒同步的。通過原子操作同樣可以實現之前的例子。



待續。。。