標籤:blog http strong ar art div 代碼 log sp
一、ManualResetEvent
該對象有兩種訊號量狀態True和False。建構函式設定初始狀態。
- WaitOne:該方法用於阻塞線程,預設是無限期的阻塞,支援逾時阻塞,如果逾時就放棄阻塞,這樣也就避免了無限期等待的尷尬;
- Set:手動修改訊號量為True,也就是恢複線程執行;
- ReSet:重設狀態;
class Program { public static void Main() { Thread t = new Thread(Run); t.Name = "輔助線程"; t.Start(); Console.WriteLine("目前時間:{0} {1}準備執行!", DateTime.Now.TimeOfDay, t.Name); //手動修改訊號量為True,也就是恢複一個等待線程執行。 mr.Set(); Console.ReadKey(); } //一開始設定為false才會等待收到訊號才執行 static ManualResetEvent mr = new ManualResetEvent(true); static void Run() { //線程開始執行時待命,收到訊號才動身 mr.WaitOne(); Console.WriteLine("\n目前時間:{0} {1}正式執行!", DateTime.Now.TimeOfDay, Thread.CurrentThread.Name); //我想讓輔助線程暫停3秒 mr.WaitOne(3000); Console.WriteLine("\n目前時間:{0} {1}暫停3秒,但是無效!", DateTime.Now.TimeOfDay, Thread.CurrentThread.Name); //我想讓輔助線程暫停 mr.Reset(); Console.WriteLine("\n目前時間:{0} {1}還是無效!", DateTime.Now.TimeOfDay, Thread.CurrentThread.Name); } }
輸出如下:
Reset()的意思其實是重設,重設後才又能再WaitOne();
class Program { public static void Main() { Thread t = new Thread(Run); t.Name = "輔助線程"; t.Start(); Console.WriteLine("目前時間:{0} {1}準備執行!", DateTime.Now.TimeOfDay, t.Name); //手動修改訊號量為True,也就是恢複一個等待線程執行。 mr.Set(); Thread.Sleep(10000); mr.Set(); Console.ReadKey(); } //一開始設定為false才會等待收到訊號才執行 static ManualResetEvent mr = new ManualResetEvent(false); static void Run() { //線程開始執行時待命,收到訊號才動身 mr.WaitOne(); Console.WriteLine("\n目前時間:{0} {1}正式執行!", DateTime.Now.TimeOfDay, Thread.CurrentThread.Name); //重設後停止才有效 mr.Reset(); //我想讓輔助線程暫停3秒 mr.WaitOne(3000); Console.WriteLine("\n目前時間:{0} {1}暫停3秒,這回有效了!", DateTime.Now.TimeOfDay, Thread.CurrentThread.Name); //重設後停止才有效 mr.Reset(); //我想讓輔助線程暫停,10後由主線程再次喚醒 mr.WaitOne(); Console.WriteLine("\n目前時間:{0} {1}暫停,但會被主線程再次喚醒,這回有效了!", DateTime.Now.TimeOfDay, Thread.CurrentThread.Name ); } }
輸出如下:
二、AutoResetEvent
AutoResetEvent與ManualResetEvent的區別在於AutoResetEvent 的WaitOne會改變訊號量的值。
比如說初始訊號量為True,如果WaitOne逾時訊號量將自動變為False,而ManualResetEvent則不會。
class Program { public static void Main() { Thread t = new Thread(Run); t.Name = "輔助線程"; t.Start(); Console.WriteLine("目前時間:{0} {1}準備執行!", DateTime.Now.TimeOfDay, t.Name); Console.ReadKey(); } static AutoResetEvent ar = new AutoResetEvent(true); static void Run() { var state = ar.WaitOne(1000); Console.WriteLine("當前的訊號量狀態:{0}", state); state = ar.WaitOne(1000); Console.WriteLine("再次WaitOne後現在的狀態是:{0}", state); } }
輸出如下:
假如要實現上面ManualResetEvent同樣的效果,Run方法就不用手動Reset()了:
static void Run() { //線程開始執行時待命,收到訊號才動身 mr.WaitOne(); Console.WriteLine("\n目前時間:{0} {1}正式執行!", DateTime.Now.TimeOfDay, Thread.CurrentThread.Name); //我想讓輔助線程暫停3秒 mr.WaitOne(3000); Console.WriteLine("\n目前時間:{0} {1}暫停3秒,這回有效了!", DateTime.Now.TimeOfDay, Thread.CurrentThread.Name); //我想讓輔助線程暫停,10後由主線程再次喚醒 mr.WaitOne(); Console.WriteLine("\n目前時間:{0} {1}暫停,但會被主線程再次喚醒,這回有效了!", DateTime.Now.TimeOfDay, Thread.CurrentThread.Name); }
少了手動Reset()代碼。
三、Semaphore
用於控制線程的訪問數量,預設的建構函式為initialCount和maximumCount,表示預設設定的訊號量個數和最大訊號量個數。當你WaitOne的時候,訊號量自減,當Release的時候,訊號量自增,然而當訊號量為0的時候,後續的線程就不能拿到WaitOne了,所以必須等待先前的線程通過Release來釋放。
class Program { static void Main(string[] args) { Thread t1 = new Thread(Run1); t1.Start(); Thread t2 = new Thread(Run2); t2.Start(); Thread t3 = new Thread(Run3); t3.Start(); Console.Read(); } //初始可以授予2個線程訊號,因為第3個要等待前面的Release才能得到訊號 static Semaphore sem = new Semaphore(2, 10); static void Run1() { sem.WaitOne(); Console.WriteLine("大家好,我是Run1" + DateTime.Now.TimeOfDay); } static void Run2() { sem.WaitOne(); Console.WriteLine("大家好,我是Run2" + DateTime.Now.TimeOfDay); //兩秒後 Thread.Sleep(2000); sem.Release(); } static void Run3() { sem.WaitOne(); Console.WriteLine("大家好,我是Run3" + DateTime.Now.TimeOfDay); } }
輸出:
在以上的方法中Release()方法相當於自增一個訊號量,Release(5)自增5個訊號量。但是,Release()到建構函式的第二個參數maximumCount的值就不能再自增了。
命名Semaphore可用於進程級互動。
class Program { static void Main(string[] args) { Thread t1 = new Thread(Run1); t1.Start(); Thread t2 = new Thread(Run2); t2.Start(); Console.Read(); } //初始可以授予2個線程訊號,因為第3個要等待前面的Release才能得到訊號 static Semaphore sem = new Semaphore(3, 10, "命名Semaphore"); static void Run1() { sem.WaitOne(); Console.WriteLine("進程:" +Process.GetCurrentProcess().Id + " 我是Run1" + DateTime.Now.TimeOfDay); } static void Run2() { sem.WaitOne(); Console.WriteLine("進程:" + Process.GetCurrentProcess().Id + " 我是Run2" + DateTime.Now.TimeOfDay); } }
輸出如下:
- ManualResetEvent:每次可以喚醒一個或多個線程;
- AutoResetEvent:每次只能喚醒一個線程;
訊號量 <第六篇>