在情景一、情景二中,我分別介紹了當多線程遇到 「資源爭用」、「限量使用」 情形時的解決方案,本篇是本系列的最後一種情形,會介紹幾種用於解決線程通訊的方案。html
情景三:我讓你動,你才能動!多線程
大錘:「老闆,拿這個手機讓我看看」。post
大錘:「這是手機嗎??? 分別就只是一個殼子」。性能
老闆:「呀,這多是生產上出了問題,我給你換一個!」spa
大錘:「老闆,你這是當我是傻子呢?仍是傻子呢?仍是傻子呢? 這回給個人手機怎麼沒有電源啊!我要怎麼開機啊!」操作系統
萬萬沒想到,通過千挑萬選,最終仍是找到了一個配件完整的手機。線程
老闆回去後發現了緣由是:生產和上線銷售兩個環節沒有搭配好,當生產的環節尚未結束時,就把中間產物拿去銷售了。code
解決辦法:全部動做不能擅自執行,必須服從命令,當生產環節完成時會通知上線環節,而後才被容許拿到市場上去銷售。htm
問題抽象:當某個操做的執行必須依賴於另外一個操做的完成時,須要有個機制來保證這種前後關係。對象
線程通訊方案:ManualResetEventSlim、ManualResetEvent、AutoResetEvent
方案特性:提供線程通知的能力,沒有接到通知前,線程必須等待,有前後順序。
各方案間的區別
在繼續閱讀前,請確保你已經對用戶模式構造、內核模式構造和混合模式構造有所瞭解,若是還沒有了解,建議您先閱讀情景一中相關章節。
ManualResetEvent 和 AutoResetEvent 都繼承自 EventWaitHandle 並最終與 Mutex 和 Semaphore 同樣擁有共同的祖宗: WaitHandle。在前面幾篇中我有講過 WaitHandle 是一個抽象類,包裝了 Windows 操做系統的內核對象句柄。
ManualResetEvent: 中文理解就是手動重置事件(這裏的事件並非咱們一般意義中按鈕的那種事件,更多的應該理解爲一個通告)。全部事件的初始狀態都爲 「不可用」,任何在等待該事件的線程都將一直等待下去。只有當經過 Set 方法釋放了一個信號後,等待該事件的線程才被容許執行所需的操做。若是不手動重置,那麼狀態一直爲 「可用」,任何等待該事件的線程能夠繼續執行操做。只有當調用 Reset 方法後,狀態纔會變爲 「不可用」。經過 WaitOne 來請求狀態,從而決定是否執行操做。
這個特色,有點相似紅綠燈,當綠燈亮起,全部機動車都被容許經過,直到再次變成紅燈。
與 Mutex 不一樣的是,Reset 和 Set 操做能夠由不一樣的線程發起。如:
ManualResetEvent s = new ManualResetEvent(false); Task.Factory.StartNew(() => { s.Set(); }); Task.Factory.StartNew(() => { s.Reset(); }); Task.Factory.StartNew(() => { s.WaitOne(); });
優勢:提供線程間通訊的能力,能夠跨進程使用。
缺點:速度慢於用戶模式、混合模式構造,稍快於 mutex。
AutoResetEvent: 顧名思義,就是自動重置事件。若是 ManulResetEvent 至關於紅綠燈,那 AutoResetEvent 就相似高速入口的閘機,杆擡起一次,經過一輛車。第二輛車要從新等待杆擡起。當調用 Set 後,狀態變成 「可用」,但只要一執行 WaitOne 請求狀態後,狀態便可變成 「不可用」(這個過程不須要 Reset 方法的參與)。
優勢:提供線程間通訊的能力,能夠跨進程使用。
缺點:速度慢於用戶模式、混合模式構造,稍快於 mutex。
在 .Net 4.0 時候引入了 ManualResetEventSlim 來提升性能。下面是 MSDN 的原話:
在 .NET Framework 4 版中,當等待時間預計很是短時,而且當事件不會跨越進程邊界時,可以使用 System.Threading.ManualResetEventSlim 類以得到更好的性能。當等待事件變爲已發出信號狀態的過程當中,ManualResetEventSlim 短期內會使用繁忙旋轉。 當等待時間很短時,旋轉的開銷相對於使用等待句柄來進行等待的開銷會少不少。 可是,若是事件在某個時間段內沒有變爲已發出信號狀態,則 ManualResetEventSlim 會採用常規的事件處理等待。
ManualResetEventSlim 的用法與 ManualResetEvent 幾乎類似,只是原先使用 WaitOne 的地方須要使用 Wait 代替。
優勢:提供線程間通訊的能力。
缺點:不能跨進程使用,速度快於內核模式構造。
本篇文章所解決的是當兩個或多個線程之間須要按某種順序執行的時候,線程間的同步問題。若是在開發中遇到兩個線程須要按某種順序前後執行的,則應該考慮使用 ManualResetEvent 或 AutoResetEvent。
本文來自《C# 基礎回顧: 線程同步的情景之三》