## 引言 上文[編碼技巧 同步鎖對象的選定](url)中,提到了在C#中,讓線程同步有兩種方式: - 鎖(lock、Monitor等) - 信號量(EventWaitHandle、Semaphore、Mutex) 加鎖是最常用的線程同步的方法,就不再討論,本篇主要討論使用信號量同步線程。 ## W ...
引言
上文編碼技巧 --- 同步鎖對象的選定中,提到了在C#中,讓線程同步有兩種方式:
- 鎖(lock、Monitor等)
- 信號量(EventWaitHandle、Semaphore、Mutex)
加鎖是最常用的線程同步的方法,就不再討論,本篇主要討論使用信號量同步線程。
WaitHandle介紹
實際上,再C#中 EventWaitHandle
、 Semaphore
、 Mutex
都是抽象類 WaitHandle
的派生類,它提供了一組等待信號的方法和屬性。如下圖:
主要包含靜態方法 SignalAndWait()
,WaitAll()
,WaitAny()
及一個虛方法WaitOne()
。下麵介紹一個這幾個方法。
介紹這些方法之前,先簡單介紹一下 WaitHandle
的派生類 EventWaitHandle
,該派生類有兩個實現類 AutoResetEvent
和 ManualResetEvent
,其方法列表如下:
重點說一下,Set()
和Reset()
:
- Set()方法設置事件為有信號狀態:當調用
Set()
時,它將被設置為終止狀態,並允許一個或多個等待該事件的線程繼續執行。 - Reset()方法設置事件為無信號狀態:當調用
Reset()
時,它將被設置為非終止狀態,所有想要等待該事件的線程都將被阻塞,直到調用Set()
方法使其變為終止狀態。
註意:這裡的有信號,無信號的意思類似於紅綠燈,有信號你才能夠通行,對於線程來說,有信號意味著可以接著往下運行,無信號則阻塞等待信號。
接下來的代碼段演示皆使用 AutoResetEvent
進行演示。
SignalAndWait()
當調用 WaitHandle
的靜態方法 SignalAndWait()
時,會使得當前線程等待一個 WaitHandle
對象的信號,同時設置另一個 WaitHandle
對象為有信號狀態。當第一個 WaitHandle
對象收到信號時,當前線程繼續執行,同時第二個 WaitHandle
對象變為無信號狀態。
static AutoResetEvent event1 = new AutoResetEvent(false);
static AutoResetEvent event2 = new AutoResetEvent(false);
static void Main(string[] args)
{
Thread t1 = new Thread(new ThreadStart(Worker1));
Thread t2 = new Thread(new ThreadStart(Worker2));
t1.Start();
t2.Start();
Console.ReadLine();
}
static void Worker1()
{
Console.WriteLine("線程1開始執行……");
event1.WaitOne(); // 等待事件1的發生
Console.WriteLine("線程1收到事件1的信號,繼續執行……");
WaitHandle.SignalAndWait(event1, event2); // 發送事件2的信號並等待事件2的發生
Console.WriteLine("線程1收到事件2的信號,繼續執行……");
}
static void Worker2()
{
Console.WriteLine("線程2開始執行……");
Thread.Sleep(2000); // 模擬線程2的執行時間
Console.WriteLine("線程2發出事件1的信號……");
event1.Set(); // 發送事件1的信號
Thread.Sleep(2000); // 模擬線程2的執行時間
Console.WriteLine("線程2發出事件2的信號……");
WaitHandle.SignalAndWait(event2, event1); // 發送事件1的信號並等待事件1的發生
Console.WriteLine("線程2收到事件1的信號,繼續執行……");
}
輸出:
線程1開始執行……
線程2開始執行……
線程2發出事件1的信號……
線程1收到事件1的信號,繼續執行……
線程2發出事件2的信號……
線程2收到事件1的信號,繼續執行……
線程1收到事件2的信號,繼續執行……
WaitAll()
當調用 WaitHandle
的靜態方法 WaitAll()
時,它可以等待多個WaitHandle對象的信號,直到所有對象都收到信號或等待超時。
static AutoResetEvent[] events = new AutoResetEvent[3]
{
new AutoResetEvent(false),
new AutoResetEvent(false),
new AutoResetEvent(false)
};
static void Main(string[] args)
{
Thread t1 = new Thread(new ThreadStart(Worker1));
Thread t2 = new Thread(new ThreadStart(Worker2));
t1.Start();
t2.Start();
Console.ReadLine();
}
static void Worker1()
{
Console.WriteLine("線程1開始執行……");
WaitHandle.WaitAll(events); // 等待所有事件的發生
Console.WriteLine("線程1收到所有事件的信號,繼續執行……");
}
static void Worker2()
{
Console.WriteLine("線程2開始執行……");
Thread.Sleep(2000); // 模擬線程2的執行時間
Console.WriteLine("線程2發出事件1的信號……");
events[0].Set(); // 發送事件1的信號
Thread.Sleep(2000); // 模擬線程2的執行時間
Console.WriteLine("線程2發出事件2的信號……");
events[1].Set(); // 發送事件2的信號
Thread.Sleep(2000); // 模擬線程2的執行時間
Console.WriteLine("線程2發出事件3的信號……");
events[2].Set(); // 發送事件3的信號
}
輸出:
線程1開始執行……
線程2開始執行……
線程2發出事件1的信號……
線程2發出事件2的信號……
線程2發出事件3的信號……
線程1收到所有事件的信號,繼續執行……
WaitAny()
當調用 WaitHandle
的靜態方法 WaitAny()
時,它可以等待多個WaitHandle對象中的任意一個對象收到信號,直到有一個對象收到信號或等待超時。
static AutoResetEvent[] events = new AutoResetEvent[3]
{
new AutoResetEvent(false),
new AutoResetEvent(false),
new AutoResetEvent(false)
};
static void Main(string[] args)
{
Thread t1 = new Thread(new ThreadStart(Worker1));
Thread t2 = new Thread(new ThreadStart(Worker2));
t1.Start();
t2.Start();
Console.ReadLine();
}
static void Worker1()
{
Console.WriteLine("線程1開始執行……");
WaitHandle.WaitAny(events); // 等待任意事件的發生
Console.WriteLine("線程1收到任意事件的信號,繼續執行……");
}
static void Worker2()
{
Console.WriteLine("線程2開始執行……");
var randomIndex = new Random().Next(0, 2);
Console.WriteLine("線程2發出任意一個事件的信號……");
events[randomIndex].Set(); //發送任意一個事件的信號
}
輸出:
線程1開始執行……
線程2開始執行……
線程2發出任意一個事件的信號……
線程1收到任意事件的信號,繼續執行……
WaitOne()
WaitOne()
方法上文中其實已經用到了,它就表示阻塞當前線程,等待當前 WaitHandle
對象收到信號,直到對象收到信號或等待超時。如果WaitHandle對象收到信號,WaitOne()方法返回true,否則返回false。使用簡單就不在貼代碼段。
派生類的異同
上面已經提到了EventWaitHandle
、 Semaphore
、 Mutex
都是抽象類 WaitHandle
的派生類,它們的作用類似,但在使用和實現上有一些不同。下麵我們來簡單介紹下它們的異同點。
-
EventWaitHandle:
EventWaitHandle
有兩種類型:AutoResetEvent
和ManualResetEvent
。它們的區別在於AutoResetEvent
在有信號時只通知一個等待線程,而ManualResetEvent
在有信號時通知所有等待線程。
兩者設置為終止狀態的方式都是調用Set()
方法。 -
Semaphore
Semaphore
可以用於多個線程之間的資源控制。Semaphore
可以控制同時訪問共用資源的線程數量。設置為終止狀態的方式是調用Release()
方法。 -
Mutex
Mutex
可以用於多個線程之間的互斥訪問共用資源。Mutex
可以保證同一時間只有一個線程可以訪問共用資源。設置為終止狀態的方式是調用ReleaseMutex()
方法。
作者: Niuery Daily
出處: https://www.cnblogs.com/pandefu/>
關於作者:.Net Framework,.Net Core ,WindowsForm,WPF ,控制項庫,多線程
本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出 原文鏈接,否則保留追究法律責任的權利。 如有問題, 可郵件咨詢。