1. 類的關系
- AutoResetEvent 和 ManualResetEvent 都繼承自 System.Threading.EventWaitHandle 類(EventWaitHandle 繼承自 WaitHandle);
- 用于線程互動 (或線程信号)
2. 常用方法
2.1 WaitHandle 幾個常用的方法
- public virtual bool WaitOne(int millisecondsTimeout); //阻止目前線程 ,直到目前 System.Threading.WaitHandle 收到信号,或直到 millisecondsTimeout 後。
- public virtual bool WaitOne(); //阻止目前線程,直到目前 System.Threading.WaitHandle 收到信号。
- public static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout);
- public static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout);
2.2 EventWaitHandle 幾個常用的方法
-
public bool Set();
将事件設定為有信号,進而允許一個或多個等待線程繼續執行
-
public bool Reset();
将事件設定為非終止狀态,進而導緻線程受阻
2.3 AutoResetEvent
- 該類的定義:通知正在等待的線程已發生事件
- public AutoResetEvent(bool initialState); //initialState 指是否将初始狀态設定為終止狀态的類
2.4 ManualResetEvent
- 該類的定義:通知一個或多個正在等待的線程已發生事件
- public ManualResetEvent(bool initialState);
3. 使用邏輯
3.1 AutoResetEvent 的使用邏輯
- 執行個體化 AutoResetEvent 時,需要設定初始狀态是否為終止狀态;
- 若遇到狀态為非終止狀态的 AutoResetEvent 的 Wait 系列方法(如 WaitOne()),将 阻止目前線程繼續往下執行,直到目前 AutoResetEvent 收到信号;
- 收到信号後,保持終止狀态并釋放一個等待線程,讓其繼續往下執行,然後自動傳回非終止狀态。
3.2 ManualResetEvent 的使用邏輯
- 執行個體化 ManualResetEvent 時,需要設定初始狀态是否為終止狀态;
- 若遇到狀态為非終止狀态的 ManualResetEvent 的 Wait 系列方法(如 WaitOne()),将 阻止目前線程繼續往下執行,直到目前 ManualResetEvent 收到信号;
- 收到信号後,所有等待線程都被釋放,可以繼續往下執行;
- 跟 AutoResetEvent 不同的是,收到信号後,ManualResetEvent 将繼續保持信号,直到通過手動調用 Reset() 方法重置 ManualResetEvent 的狀态為非終止。
4. 例子
4.1 AutoResetEvent
using System;
using System.Threading;
class Example
{
private static AutoResetEvent event_1 = new AutoResetEvent(true);
private static AutoResetEvent event_2 = new AutoResetEvent(false);
static void Main()
{
for (int i = 1; i < 4; i++)
{
Thread t = new Thread(ThreadProc);
t.Name = "Thread_" + i;
t.Start();
}
Thread.Sleep(250);
for (int i = 0; i < 2; i++)
{
Console.WriteLine("按任意鍵釋放另一個線程");
Console.ReadLine();
event_1.Set();
Thread.Sleep(250);
}
Console.WriteLine("\r\n至此所有線程都在等待 AutoResetEvent #2.");
for (int i = 0; i < 3; i++)
{
Console.WriteLine("按任意鍵釋放另一個線程");
Console.ReadLine();
event_2.Set();
Thread.Sleep(250);
}
}
static void ThreadProc()
{
string name = Thread.CurrentThread.Name;
Console.WriteLine("{0} 等待 AutoResetEvent #1.", name);
event_1.WaitOne();
Console.WriteLine("{0} 被 AutoResetEvent #1 釋放.", name);
Console.WriteLine("{0} 等待 AutoResetEvent #2.", name);
event_2.WaitOne();
Console.WriteLine("{0} 被 from AutoResetEvent #2 釋放.", name);
Console.WriteLine("{0} 結束.", name);
}
}
/* 上面例子将輸出如下:
Thread_1 等待 AutoResetEvent #1.
Thread_1 被 AutoResetEvent #1 釋放.
Thread_1 等待 AutoResetEvent #2.
Thread_2 等待 AutoResetEvent #1.
Thread_3 等待 AutoResetEvent #1.
按任意鍵釋放另一個線程
Thread_2 被 AutoResetEvent #1 釋放.
Thread_2 等待 AutoResetEvent #2.
按任意鍵釋放另一個線程
Thread_3 被 AutoResetEvent #1 釋放.
Thread_3 等待 AutoResetEvent #2.
至此所有線程都在等待 AutoResetEvent #2.
按任意鍵釋放另一個線程.
Thread_1 被 from AutoResetEvent #2 釋放.
Thread_1 結束.
按任意鍵釋放另一個線程.
Thread_2 被 from AutoResetEvent #2 釋放.
Thread_2 結束.
按任意鍵釋放另一個線程.
Thread_3 被 from AutoResetEvent #2 釋放.
Thread_3 結束.
*/
4.2 ManualResetEvent
using System;
using System.Threading;
public class Example
{
private static ManualResetEvent mre = new ManualResetEvent(false);
static void Main()
{
Console.WriteLine("\n開啟 3 個被 ManualResetEvent 阻塞的線程\n");
for (int i = 0; i <= 2; i++)
{
Thread t = new Thread(ThreadProc);
t.Name = "Thread_" + i;
t.Start();
}
Thread.Sleep(500);
Console.WriteLine("\n所有線程已開啟,請按下 enter 鍵發送信号進而一次性釋放所有線程\n");
Console.ReadLine();
mre.Set();
Thread.Sleep(500); // 主線程等待 500 毫秒讓子線程先執行完畢
Console.WriteLine("\n當 ManualResetEvent 是有信号時,即使遇到 WaitOne() 也不會被阻塞:\n");
for (int i = 3; i <= 4; i++)
{
Thread t = new Thread(ThreadProc);
t.Name = "Thread_" + i;
t.Start();
}
Thread.Sleep(500);
Console.WriteLine("\n請按下 enter 鍵盤觸發 Reset() 方法, 進而後面的線程在遇到 WaitOne() 時繼續被阻塞.\n");
Console.ReadLine();
mre.Reset();
Thread t5 = new Thread(ThreadProc);
t5.Name = "Thread_5";
t5.Start();
Thread.Sleep(500);
Console.WriteLine("\n請按下 enter 鍵發送信号.");
Console.ReadLine();
mre.Set();
Console.ReadLine();
}
private static void ThreadProc()
{
string name = Thread.CurrentThread.Name;
Console.WriteLine(name + " starts and calls mre.WaitOne()");
mre.WaitOne();
Console.WriteLine(name + " ends.");
}
}
/* 上面例子将輸出如下:
開啟 3 個被 ManualResetEvent 阻塞的線程
Thread_0 starts and calls mre.WaitOne()
Thread_1 starts and calls mre.WaitOne()
Thread_2 starts and calls mre.WaitOne()
所有線程已開啟,請按下 enter 鍵發送信号進而一次性釋放所有線程
Thread_0 ends.
Thread_1 ends.
Thread_2 ends.
當 ManualResetEvent 是有信号時,即使遇到 WaitOne() 也不會被阻塞:
Thread_3 starts and calls mre.WaitOne()
Thread_3 ends.
Thread_4 starts and calls mre.WaitOne()
Thread_4 ends.
請按下 enter 鍵盤觸發 Reset() 方法, 進而後面的線程在遇到 WaitOne() 時繼續被阻塞.
Thread_5 starts and calls mre.WaitOne()
請按下 enter 鍵發送信号.
Thread_5 ends.
*/