你可能在上篇文章中《深入多線程之:雙向信號與競賽的用法分析》注意到了這個模式:兩個Waiting 循環都要下面的構造:
lock(_locker)
{
while(!_flag) Monitor.Wait(_locker);
_flag = false;
}
讓我們使用Wait和Pulse來為ManualResetEvent完成剩余的代碼吧。
void WaitOne()
{
lock (_locker)
{
while (!_signal) Monitor.Wait(_locker);
}
}
void Set()
{
lock (_locker) { _signal = true; Monitor.PulseAll(_locker); }
}
void Reset() { lock (_locker) _signal = false; }
如果在WaitOne方法中增加_signal=false就可以簡單的模擬AutoResetEvent.例如:
Lock(_locker) {_signal = true; Monitor.Pulse(_locker);}
如果使用的是int類型的_signal 標志,那么我們可以得到一個最基本的Semaphore.
Waiting Queues and PulseAll
當多余一個線程在同一個對象上面等待的時候,一個 “等待隊列(waiting queue)” 就形成了。
每一次調用Pulse都會釋放在”等待隊列”頭部的一個線程。下面的圖形象的展示了這一點:
線程調用Monitor.Enter 進入ReadyQueue. 等待獲取鎖,成功獲取鎖后,如果正常的執行,那么之后會調用Monitor.Exit退出,
否則如果獲取了鎖之后發現需要等待其他的線程或者是其他阻塞條件,那么調用Wait方法,就進入了等待隊列,
當等待的線程完成并調用Pulse后,處在WaitingQueue頭部的線程就被 Pulse了,等待CPU調度 。之后再次進入Ready Queue,重新獲取鎖。
Countdown
借助Wait和Pulse,我們可以實現CountdownEvent的主要功能。例如:
public void Singnal() { AddCount(-1); } //將計數減一
public void AddCount(int amount)
{
lock (_locker)
{
_value += amount; //將計數增加或減少
if (_value <= 0) Monitor.PulseAll(_locker);//如果value<=0,說明所有等待的任務都完成了。
}
}
public void Wait()
{
lock (_locker)
{
//只要計數 > 0 就等待。
while (_value > 0)
{
Monitor.Wait(_locker);
}
}
}
}
新聞熱點
疑難解答