C# 溫故而知新: 線程篇(四)
線程同步篇 (中):同步工具類的介紹
- 1 上篇回顧
- 2 繼續介紹基元核心模式中的 monitor類
- 3 同步控制代碼:WaitHandle
- 4 EventWaitHandle,AutoResetEvent和ManualResetEvent
- 5 同步互斥mutex類
- 6 簡單說明下mutex和monitor的區別
- 7 選擇我們需要的同步工具
- 8 本章總結
1 上篇回顧
很抱歉好久沒寫部落格了,由於工作太忙,所以最近一段時間落下了,讓我們開始上一篇向大家介紹了下線程同步中的一些重要概念包括:
基元核心模式,基元使用者模式,原子性,然後由陸續介紹了基元使用者模式中的Validated,Interloced 和ReaderWriterLock 類,同時也簡單
介紹了下基元核心模式中的lock關鍵字,本章再讓我們繼續深入瞭解其他的一些同步工具類
2 繼續介紹基元核心模式中的Monitor類
首先讓大家有個初步的印象:
Monitor類也是同步機制中比較重要的一個類,它屬於基元核心模式中的一種,也是上一章中與lock關鍵字有著密切關係,Monitor類採取
獨佔鎖定來進行對共用區的同步,當一個線程進入共用區時,會取得獨佔鎖定的控制權,其他線程則必須等待,大夥注意,這裡有2個重要的線
程狀態需要在說明下
1:等待隊列: 等待進入共用區的線程會首先進入到等待隊列中,等待持有獨佔鎖定的線程通知某個等待線程進入到就緒隊列中,注意(只有擁 有獨佔鎖定的線程才能進行互換通知功能,甚至該線程能夠喚醒一堆的等待線程進入到就緒隊列中) 2:就緒隊列 等待隊列中的某個線程被持有獨佔鎖定的線程喚醒放入到就緒隊列中,等待擷取獨佔鎖定的機會,這樣一個周期便可以串連起來, 線程從等待到被喚醒到就緒狀態,然後擷取獨佔鎖定進如共用區操作,然後交出獨佔鎖定等待或者睡眠,直到再次被喚醒。 |
在這裡強調下Monitor是個十分容易產生死結的同步類,其原因是:
1.當一個線程試圖去請求鎖對象時,它不是處在等待隊列,而是就緒隊列,如果需要讓其進入等待隊列,則必須使用Wait方法
2.當一個線程釋放鎖對象時是不會通知等待隊列中的線程進入到就緒隊列,需要通過Palse方法
3.線程啟動的時候,線程是處於就緒狀態的
4.就算處於就緒狀態的線程被cpu選中,但是一旦資料被鎖定,那個線程還是無法擷取到控制權
其實大家瞭解原因後對於monitor已經算瞭解,這正是monitor的機制
瞭解了上述機制後,大家可以開始理解該類比較重要的幾個方法:
Monitor. Enter(Object);
該方法旨在宣布當前線程進入了臨界區,持有了獨佔鎖定,其他線程繼續等待,直到該線程離開共用區交出獨佔鎖定給就緒隊列中的一個線程
Monitor. Exit(Object);
當持有共用鎖定的線程執行完任務之後,該線程變通過這個方法離開共用區,臨走前可以操作其喚醒一個或一堆的等待線程
Monitor.Palse(Object)和Monitor.Palse(Object)
這兩個方法比較複雜和相似,也就是喚醒(改變)其他線程狀態的方法,持有獨佔鎖定的線程利用這兩個方法通知其他線程進入到就緒隊列,離開等待隊列
Monitor.Wait(Object)
這個方法也是非常的重要,假如在共用區內的線程執行了wait方法後,該線程會被放入等待隊列中,從而失去了獨佔鎖定的控制權,就緒隊列中的下一個線程就進入到了臨界區
Monitor.TryEnter(Object, Boolean))
有時其他線程希望通過進行嘗試的方式主動去爭取獨佔鎖定的控制權,這個方法便能實現這個功能,同時通過一個BOOL參數來指示會否佔有了獨佔鎖定
文字介紹為了讓大夥更好的理解Monitor類的本質,接下來就上很簡單代碼讓我們更深入的瞭解
/// <summary> /// 開啟2個寫線程,2個讀線程示範 /// </summary> /// <param name="args"></param> static void Main(string[] args) { Thread t1 = new Thread(new ThreadStart(WriteInShareArea)); Thread t3 = new Thread(new ThreadStart(WriteInShareArea)); Thread t2 = new Thread(new ThreadStart(Read)); Thread t4= new Thread(new ThreadStart(Read)); Console.WriteLine("t1's id={0}", t1.ManagedThreadId); Console.WriteLine("t2's id={0}", t2.ManagedThreadId); Console.WriteLine("t3's id={0}", t3.ManagedThreadId); t1.Start(); t3.Start(); t2.Start(); t4.Start(); Console.Read(); } /// <summary> /// 讀資料,首先是先讓讀線程等待,當寫線程寫完執行 Pulse 方法後,喚醒讀線程繼續工作 /// </summary> private static void Read() { while (true) { Monitor.Enter(lockObj);
//讀線程啟動時預設就緒隊列 //讀線程執行到這裡時會掛起進入等待隊列 Monitor.Wait(lockObj, 4000); Console.WriteLine("Thread{0} can read nao", Thread.CurrentThread.ManagedThreadId); Console.WriteLine("Thread{0} reading...............", Thread.CurrentThread.ManagedThreadId); Thread.Sleep(3000); Monitor.Exit(lockObj); } } /// <summary> /// 寫資料,當寫線程寫完執行 Pulse 方法後,喚醒讀線程繼續工作 /// </summary> private static void WriteInShareArea() { while (true) { Thread.Sleep(1000); Monitor.Enter(lockObj); Console.WriteLine("Thread{0} change data nao", Thread.CurrentThread.ManagedThreadId); Console.WriteLine("Thread{0} changing...............", Thread.CurrentThread.ManagedThreadId); Thread.Sleep(1000); //喚醒讀線程進入就緒隊列 Monitor.Pulse(lockObj); Monitor.Exit(lockObj); } }
執行結果:
3 同步控制代碼:WaitHandle
WaitHandle可以說是本章中許多同步對象的基類包括前後文中的AutoResetEvent,ManualResetEvent,mutex 和Semaphore都是其子類,
waitHandle是個抽象類別,主要的功能從字面上就能看的出來,等待控制代碼,不錯 WaitHandle非常的神奇,它容納了一個WIN32的核心物件控點,線程
會等待核心對象的訊息,當然核心對象也會等待接收訊號,一旦接收到訊號則會通知當前線程,這是一種複雜的作業系統調度原理,大家可以參考
WINDOWS核心編程等書繼續深入瞭解下,本文旨在說明下waitHandle的概念和一些簡單的介紹,這裡在為waitHandle打個比方:
當你想穿過馬路時,如果號誌為紅色則你只能等待,如果轉變成綠燈的話,你就可以通行,其實waitHandle的作用就是紅綠燈的作用,聰明的的你
肯定想到了,那我們可以將線程同步號誌放入線程池中嗎?哈哈當然可以,不僅可以放一根,而且可以放入一堆WaitHadle對象。言歸正傳:WaitHadle
也有2個訊號” Signaled” and “NonSignaled” ,前者可以理解為綠燈,綠燈狀態時WaitOne方法無效,當前線程不會被阻止,後者可以理解為紅燈,當底
層核心對象接受到訊號時,通知某個當前線程可以進入到共用區(臨界區)其餘的必須等待,不過基於抽象類別的考慮,waitHandle沒有喚醒線程的set方法,
而讓其子類實現了,所以WaitHandle 還是完全體現其”等啊等,直到提示可以通過或者直接奔潰(逾時異常)”的特色
既然waitHandle只有阻塞等待的作用,那我們來看下它的幾個奇妙的方法:
1 bool WaitOne(): 等待一個通過當前waitHandle指定的核心對象收到訊號後返回true,否則返回false
2 bool WaitAll():等待waitHandle[]中所有的核心對象都收到訊號後返回true,否則返回false
3 bool WaitAny();等待waitHandle[]中核心對象都收到訊號後返回true,否則返回false
4 bool SignalAndWait():這個方法無法用文字表達清楚,但是大夥先可以理解成這樣:自動的向核心對象發送資訊,等待另一個核心對象接收到訊號,
如果另一個核心對象接受到訊號則返回TRUE,這牽涉到了複雜的”混合鎖”的機制,所以本文不再詳細說明,後章會詳細介紹混合鎖機制
接著讓我們來看下WaitHandle的幾個衍生類別結構,後文中將一一介紹:
EventWaitHandle
AutoResetEvent
ManualResetEvent
Mutex
Semaphore (將在下章詳細介紹)
最後上一個圖來表示下WaitHandle的工作原理