Wait() 和 Pulse() 機制用于線程間互動。當在一個對象上使用Wait() 方法時,通路這個對象的線程就會一直等待直到被喚醒。Pulse() 和 PulseAll() 方法用來通知等待的線程醒來的。下面是關于Wait() 和 Pulse() 方法如何運作的例子,WaitAndPulse.cs:
Wait() 和 Pulse() 方法僅可以在Enter() 和 Exit() 代碼塊内部
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace ThreadEx
{
public class LockMe
{
}
class WaitPalsel
{
private int result = 0;
private LockMe lM;
public WaitPalsel(LockMe l)
{
this.lM = l;
}
public void CriticalSection()
{
Monitor.Enter(this.lM);
Console.WriteLine("WaitPulse1: Entered Thread " + Thread.CurrentThread.GetHashCode());
for (int i = 1; i <= 5; i++)
{
Monitor.Wait(this.lM);
Console.WriteLine("WaitPulse1: Result =" + result++ + "ThreadID"
+ Thread.CurrentThread.GetHashCode());
Monitor.Pulse(this.lM);
}
Console.WriteLine("WaitPulse1: Exiting Thread " + Thread.CurrentThread.GetHashCode());
Monitor.Exit(this.lM);
}
}
class WaitPulse2
{
private int result = 0;
private LockMe lM;
public WaitPulse2()
{ }
public WaitPulse2(LockMe l)
{
this.lM = l;
}
public void CriticalSection()
{
Monitor.Enter(this.lM);
Console.WriteLine("WaitPulse2: Entered Thread " + Thread.CurrentThread.GetHashCode());
for (int i = 1; i < 5; i++)
{
Monitor.Pulse(this.lM);
Console.WriteLine("WaitPulse2: Result ="+result++ +"ThreadID"
+Thread.CurrentThread.GetHashCode());
Monitor.Wait(this.lM);
Console.WriteLine("WaitPulse2: WokeUp");
}
Console.WriteLine("WaitPulse2 Exiing Thread " + Thread.CurrentThread.GetHashCode());
Monitor.Exit(this.lM);
}
}
class Program
{
static void Main(string[] args)
{
LockMe l = new LockMe();
WaitPalsel e1 = new WaitPalsel(l);
WaitPulse2 e2 = new WaitPulse2(l);
Thread t1 = new Thread(new ThreadStart(e1.CriticalSection));
t1.Start();
Thread t2 = new Thread(new ThreadStart(e2.CriticalSection));
t2.Start();
Console.ReadLine();
}
}
}
在Main() 方法中,我們建立了一個LockMe對象。然後建立了兩個對象,WaitPulse1, WaitPulse2, 接着把它們委托給線程以便于線程可以調用這兩個對象的CriticalSection()方法。注意WaitPulse1和WaitPulse2這兩個對象中的LockMe執行個體是不同的,因為傳遞給對應構造函數的對象引用不同。初始化完對象以後,我們建立了兩個線程t1 和 t2, 并向這兩個線程分别傳遞各自的CriticalSection()函數。
假設WaitPulse1.CriticalSection() 先執行,線程t1 進入方法的關鍵部分并在鎖住LockMe對象後在for循環中執行Monitor.Wait()。由于執行了Monitor.Wait(), 是以它得等待其他線程調用Monitor.Pulse()方法(一個運作時通知)來将其喚醒。我們鎖住LockMe對象是因為我們隻希望在任意時間僅有一個對象通路共享LockMe執行個體。
注意當線程執行Monitor.Wait()方法時,它會暫時釋放LockMe對象上的鎖,這樣其他線程就可以通路LockMe對象。線上程t1進入等待狀态後,線程t2可以自由地通路LockMe對象。盡管這兩個線程都有自己的LockMe對象(WaitPulse1, WaitPulse2),但是它們都引用同一個對象。線程t2獲得LockMe對象上的鎖并進入WaitPulse2.CriticalSection()方法。當它進入for循環時,它給等待LockMe對象的線程(本例中是t1)發送一個運作時通知(Monitor.Pulse())然後進入等待狀态。
最終,t1醒來并獲得LockMe對象的鎖。線程t1然後通路result變量并向等待LockMe對象的線程(本例中為t2)發送一個運作時通知。如此反複直到for循環結束。
如果你依據程式的輸出結果來看上面的描述,那麼我們說的概念會非常清晰易懂。要注意每個Enter()方法都有一個Exit()方法比對否則程式不應該結束。
Enter()方法接受一個對象作為參數。如果參數為null 或者參數時一個方法名或者一個值類型的對象(比如int型),Enter()方法都會抛出異常。
調用。