我們在編程的時候,有時會使用多線程來解決問題,比如你的程序需要在 后臺處理一大堆數據,但還要使用戶界面處于可操作狀態;或者你的程序需要訪問一些外部資源如數據庫或網絡文件等。這些情況你都可以創建一個子線程去處理, 然而,多線程不可避免地會帶來一個問題,就是線程同步的問題。如果這個問題處理不好,我們就會得到一些非預期的結果。
在網上也看過一些關于線程同步的文章,其實線程同步有好幾種方法,下面我就簡單的做一下歸納。
一、volatile關鍵字
volatile是最簡單的一種同步方法,當然簡單是要付出代價的。它只能在變量一級做同步,volatile的含義就是告訴處理器, 不要將我放入工作內存, 請直接在主存操作我。(【轉自www.bitsCN.com】)因此,當多線程同時訪問該變量時,都將直接操作主存,從本質上做到了變量共享。
能夠被標識為volatile的必須是以下幾種類型:(摘自MSDN)
如:
但volatile并不能實現真正的同步,因為它的操作級別只停留在變量級別,而不是原子級別。如果是在單處理器系統中,是沒有任何問題的,變量在主存中沒有機會被其他人修改,因為只有一個處理器,這就叫作processor Self-Consistency。但在多處理器系統中,可能就會有問題。每個處理器都有自己的data cach,而且被更新的數據也不一定會立即寫回到主存。所以可能會造成不同步,但這種情況很難發生,因為cach的讀寫速度相當快,flush的頻率也相當高,只有在壓力測試的時候才有可能發生,而且幾率非常非常小。
二、lock關鍵字
lock是一種比較好用的簡單的線程同步方式,它是通過為給定對象獲取互斥鎖來實現同步的。它可以保證當一個線程在關鍵代碼段的時候,另一個線程不會進來,它只能等待,等到那個線程對象被釋放,也就是說線程出了臨界區。用法:
所以,使用lock應該注意以下幾點:
?。薄⑷绻粋€類的實例是public的,最好不要lock(this)。因為使用你的類的人也許不知道你用了lock,如果他new了一個實例,并且對這個實例上鎖,就很容易造成死鎖。
?。病⑷绻鸐yType是public的,不要lock(typeof(MyType))
?。场⒂肋h也不要lock一個字符串
三、System.Threading.Interlocked
對于整數數據類型的簡單操作,可以用Interlocked類的成員來實現線程同步,存在于System.Threading命名空間。Interlocked類有以下方法:Increment,Decrement,Exchange和CompareExchange。使用Increment和Decrement可以保證對一個整數的加減為一個原子操作。Exchange方法自動交換指定變量的值。CompareExchange方法組合了兩個操作:比較兩個值以及根據比較的結果將第三個值存儲在其中一個變量中。比較和交換操作也是按原子操作執行的。如:
Output:
四、Monitor
Monitor類提供了與lock類似的功能,不過與lock不同的是,它 能更好的控制同步塊,當調用了Monitor的Enter(Object o)方法時,會獲取o的獨占權,直到調用Exit(Object o)方法時,才會釋放對o的獨占權,可以多次調用Enter(Object o)方法,只需要調用同樣次數的Exit(Object o)方法即可,Monitor類同時提供了TryEnter(Object o,[int])的一個重載方法,該方法嘗試獲取o對象的獨占權,當獲取獨占權失敗時,將返回false。
但使用lock通常比直接使用Monitor更可取,一方面是因為lock更簡潔,另一方面是因為lock確保了即使受保護的代碼引發異常,也可以釋放基礎監視器。這是通過finally中調用Exit來實現的。事實上,lock就是用Monitor類來實現的。下面兩段代碼是等效的:
關于用法,請參考下面的代碼:
當線程1獲取了m_monitorObject對象獨占權時,線程2嘗試調用TryEnter(m_monitorObject),此時會由于無法獲取獨占權而返回false,輸出信息如下:
另外,Monitor還提供了三個靜態方法 Monitor.Pulse(Object o),Monitor.PulseAll(Object o)和Monitor.Wait(Object o ) ,用來實現一種喚醒機制的同步。關于這三個方法的用法,可以參考MSDN,這里就不詳述了。
五、Mutex
在使用上,Mutex與上述的Monitor比較接近,不過Mutex不具 備Wait,Pulse,PulseAll的功能,因此,我們不能使用Mutex實現類似的喚醒的功能。不過Mutex有一個比較大的特點,Mutex是 跨進程的,因此我們可以在同一臺機器甚至遠程的機器上的多個進程上使用同一個互斥體。盡管Mutex也可以實現進程內的線程同步,而且功能也更強大,但這 種情況下,還是推薦使用Monitor,因為Mutex類是win32封裝的,所以它所需要的互操作轉換更耗資源。
六、ReaderWriterLock
在考慮資源訪問的時候,慣性上我們會對資源實施lock機制,但是在某些情 況下,我們僅僅需要讀取資源的數據,而不是修改資源的數據,在這種情況下獲取資源的獨占權無疑會影響運行效率,因此.Net提供了一種機制,使用 ReaderWriterLock進行資源訪問時,如果在某一時刻資源并沒有獲取寫的獨占權,那么可以獲得多個讀的訪問權,單個寫入的獨占權,如果某一時 刻已經獲取了寫入的獨占權,那么其它讀取的訪問權必須進行等待,參考以下代碼:
新聞熱點
疑難解答