C# Monitor的Wait和Pulse方法使用詳解
【轉載】http://blog.csdn.net/qqsttt/article/details/24777553
Monitor的Wait和Pulse方法在線程的同步鎖使用中是比較復雜的,理解稍微困難些,但也是內涵相當豐富和
微妙的!通過他們你可以自己實現AutoResetEvent,ManualResetEvent等同步對象,同時還會在效率和內存
使用上有個質的提高!
今天在MSDN查閱Monitor對象時,發現其下的成員方法的Demo都是用一個安全的同步Queue來闡述的,但是
代碼註解和本身MSDN的專業術語晦澀難懂,造成這個實例並不是好理解,這裏我將註釋都添加到了關鍵語句上,
同時最後結合代碼分析這個同步Queue是如何在Monitor的Wait和Pulse的指導下工作的:
現在我對整個代碼做一個流程分析:
在這個特定背景下,線程優先順序: 【等待隊列】->【就緒隊列】->【擁有鎖線程】這個是重點,下文多
次會提到,其中的微妙關系的核心也來源於這個執行順序。
MSDN官方備註:同步的對象包含若幹引用,其中包括對當前擁有鎖的線程的引用、對就緒隊列的引
用和對等待隊列的引用。
我的提醒:競爭對象鎖的線程都是處於就緒隊列中。
在本案例中我將FirstThread和SecondThread方法看成是A,B線程(這是完全可以的,因為A,B是兩個線程的
回調函數都是同級的工作者線程),
下面是分析步驟:
/*情形1:假設A線程獲取了m_smplQueue同步對象鎖:
* 1、開始循環,調用Monitor.Wait(m_smplQueue):A線程釋放自己對同步對象的鎖,流放自己到
等待隊列(B線程一開始就競爭同步鎖所以處於就緒隊列中),直到自己再次獲得鎖,否則一直阻塞。
所以A線程運行到這裏就暫停了。
* 2、這時候B直接從就緒隊列出來獲得了m_smplQueue對象鎖,Monitor.Pulse(m_smplQueue):執
行時,會將A線程放行到就緒隊列,A準備獲取對鎖的擁有權。
* 3、執行循環,Monitor.Wait(m_smplQueue, 1000):B線程將自己流放到等待隊列並釋放自身對
同步鎖的獨占,該等待設置了1S的超時值,當B線程在1S之內沒有再次獲取到鎖則自動添加到就緒
隊列,或者這期間收到Pulse的脈沖信號。
* 4、B線程由於1S之內都返回false,lock塊迅速結束,也即退出對m_smplQueue獨占權,A由就緒
隊列中進入對m_smplQueue的獨占、繼續.
* 5、在1中陳述的Monitor.Wait(m_smplQueue)的阻塞結束,返回true,執行接下來的代碼:
m_smplQueue.Enqueue(counter)向隊列中加入元素,執行下一行的Monitor.Pulse(m_smplQueue),
由於第3條的1S沒到(我相信地球上目前已沒有這麽慢的CPU了),B線程收到脈沖,將自己添加到就
緒隊列,counter計數+1,A線程的lock結束,A則進入等待隊列.
* 6、由於B從就緒隊列再次獲得獨占權,Monitor.Wait(m_smplQueue, 1000)返回true,while進入循
環內部,彈出第一條元素,打印出來。 調用Monitor.Pulse(m_smplQueue)將A線程加入到就緒隊列,
同時while結束,lock塊結束,B退出對對象鎖的獨占進入到等待隊列中.
* 7、A繼續,遵循這個規律循環往復知道所有的數被打印出來...
*
* 情形2:B線程先獲取了m_smplQueue同步對象鎖:
* 1、進入lock塊,Monitor.Pulse(m_smplQueue)執行:由於當前的A線程已經處於就緒隊列
所以收到也沒作用(那麽你肯定再問那這句有什麽用啊?沒錯是得問,你發現沒如果是A線程開始是
否就有用了啊!這就是它的作用!).
* 2、開始while (Monitor.Wait(m_smplQueue, 1000))中的判斷,技術細節還是遵循上面所講的,B這時
候會自動將自己流放到等待隊列並在這裏阻塞(也許1S到期了也會將它放置到就緒隊列中去,這個作用
主要是防止死鎖,因為咱們的就緒隊列可不能為空啊,這在上面我忘了講了這裏補充下),於是乎A
獲得了m_smplQueue獨占權,於是乎又回到了上面從A先獲得線程鎖的流程....
*
* 總之,這個操作目的是讓多個線程操作一個Queue時,保持同步:不能在無數據時出隊,一旦有
一個數據就馬上可以出隊,最終的效果是沒有一個元素在隊列中。
*/
C# Monitor的Wait和Pulse方法使用詳解