摘自風中靈藥的博客:https://www.cnblogs.com/qingyun163/archive/2013/01/05/2846633.html#!commentshtml
AutoResetEvent和ManualResetEvent可用於控制線程暫停或繼續,擁有重要的三個方法:WaitOne、Set和Reset。函數
若是把每一個線程比做一輛汽車的話,AutoResetEvent和ManualResetEvent就是公路上的收費站。spa
其中:線程
Reset 關閉收費站車閘禁止通行(攔截車輛纔好收費啊);code
WaitOne 收費員等待下一輛車輛過來(而後收費);htm
Set 開啓收費站車閘放行(交錢了就讓過去)。blog
AutoResetEvent和ManualResetEvent的區別隊列
既然AutoResetEvent和ManualResetEvent都是收費站,那麼它們之間有什麼不一樣之處嗎?get
顧名思義,Auto即自動,Manual即手動,而Reset根據上面的比喻表示關閉車閘,也就是前者可自動關閉車閘,後者需手動關閉車閘。博客
自動關閉車閘:即一輛車交錢經過後,車閘會自動關閉,而後再等待下一輛車過來交費。即每輛車都要通過這麼幾個步驟:被阻 > 交費 > 通行 > 車閘關閉
手動關閉車閘:車閘打開後,車閘不會自動關閉,若是不手動關閉車閘(即調用ManualResetEvent.Reset()方法)的話,車輛會一輛接一輛地經過……
AutoResetEvent和ManualResetEvent的初始狀態
經過設置AutoResetEvent和ManualResetEvent構造函數可初始化收費站車閘狀態:
new Auto/ManualResetEvent(false):車閘默認關閉;
new Auto/ManualResetEvent(true): 車閘默認開啓。
若是new Auto/ManualResetEvent(true),即車閘默認開啓的話,是默認讓第一輛車經過,而後會攔截下一輛車。
static EventWaitHandle _tollStation = new AutoResetEvent(true);//車閘默認開啓 static void Main(string[] args) { new Thread(Car1).Start(); Console.ReadKey(); } static void Car1() { _tollStation.WaitOne();//因車閘默認開啓,WaitOne毫無心義,不會阻止車輛前行 Console.WriteLine("噫!車閘是開的,我過來了!"); }
此時輸出:"噫!車閘是開的,我過來了!"
可是若是改一下
static EventWaitHandle _tollStation = new AutoResetEvent(true);//車閘默認開啓 static void Main(string[] args) { new Thread(Car1).Start(); new Thread(Car1).Start();//執行兩次輸出的線程 Console.ReadKey(); } static void Car1() { _tollStation.WaitOne();//因車閘默認開啓,WaitOne毫無心義,不會阻止車輛前行 Console.WriteLine("噫!車閘是開的,我過來了!"); }
此時輸出:噫!車閘是開的,我過來了!
執行兩次線程,可是隻會輸出一次。由於第二次被阻攔了。
若是將new AutoResetEvent(true) 改成new AutoResetEvent(flase),即車閘默認爲關閉狀態的話,將不會打印任何值,即車輛沒法經過。
那如何才能經過呢?必須在主線程中調用Set方法,即打開車閘便可經過。
static EventWaitHandle _tollStation = new AutoResetEvent(false);//車閘默認關閉 static void Main(string[] args) { new Thread(Car1).Start(); _tollStation.Set();//開啓車閘 Console.ReadKey(); } static void Car1() { _tollStation.WaitOne();//等待開啓車閘,即_event.Set(); Console.WriteLine("車閘開啓,我過來了!"); }
運行將打印:
車閘開啓,我過來了!
代碼很明瞭,就不解釋了,總之就是車閘默認關閉狀態下,只有打開車閘(調用Set方法 ),車輛才能通行。
用代碼闡釋AutoResetEvent的特性
static EventWaitHandle _tollStation = new AutoResetEvent(false);//車閘默認關閉 static void Main(string[] args) { new Thread(Car1).Start();//車輛1 new Thread(Car2).Start();//車輛2 _tollStation.Set(); Console.ReadKey(); } static void Car1() { _tollStation.WaitOne();//等待開啓車閘,即_tollStation.Set(); Console.WriteLine("車輛1,順利經過。"); } static void Car2() { _tollStation.WaitOne(); Console.WriteLine("車輛2,順利經過。!"); }
運行將打印:
車輛1,順利經過。
雖然車輛1和車輛2都在運行,但只有車輛1順利經過。
由於_tollStation.Set()僅運行了一次,即車輛1經過後車閘被當即關閉,致使車輛2未被經過。
除非,在車輛1經過後再調用一次_tollStation.Set(),即再次打開車閘,車輛2才能經過:
static EventWaitHandle _tollStation = new AutoResetEvent(false);//車閘默認關閉 static void Main(string[] args) { new Thread(Car1).Start();//車輛1 new Thread(Car2).Start();//車輛2 _tollStation.Set();//開啓車閘,讓車輛1經過 Console.ReadKey(); } static void Car1() { _tollStation.WaitOne();//等待開啓車閘,即_tollStation.Set(); Console.WriteLine("車輛1,順利經過。"); _tollStation.Set();//再開啓一次車閘,讓車輛2經過 } static void Car2() { _tollStation.WaitOne(); Console.WriteLine("車輛2,順利經過。"); }
運行將打印:
車輛1,順利經過。
車輛2,順利經過。
也就是每調用一次Set,僅有一個線程會繼續。換言之,有多少個線程就要調用多少次Set,線程纔會所有繼續。
這也代表,AutoResetEvent是典型的隊列操做形式。
用代碼闡釋ManualResetEvent的特性
在上一個代碼塊中,_tollStation.Set()調用了兩次,兩輛車才順利經過。
那麼,有沒有什麼辦法,只調用一次_tollStation.Set()就讓兩輛或更多輛汽車順利經過呢?
答案是,將AutoResetEvent改成ManualResetEvent:
static EventWaitHandle _tollStation = new ManualResetEvent(false);//改成ManualResetEvent,車閘默認關閉 static void Main(string[] args) { new Thread(Car1).Start();//車輛1 new Thread(Car2).Start();//車輛2 _tollStation.Set();//開啓車閘,全部車輛都會經過 Console.ReadKey(); } static void Car1() { _tollStation.WaitOne();//等待開啓車閘,即_tollStation.Set(); Console.WriteLine("車輛1,順利經過。"); //_tollStation.Set();//這裏再也不須要了 } static void Car2() { _tollStation.WaitOne(); Console.WriteLine("車輛2,順利經過。"); }
運行將打印:
車輛1,順利經過。
車輛2,順利經過。
這很好的說明了,ManualResetEvent開啓車閘後不會自動關閉這一特性。因此調用一次_tollStation.Set(),所有車輛將順利經過。
若是在某一時刻手動關閉了車閘,則後面的車輛將沒法經過。如如下代碼:
static EventWaitHandle _tollStation = new ManualResetEvent(false);//改成ManualResetEvent,車閘默認關閉 static void Main(string[] args) { new Thread(Car1).Start();//車輛1 new Thread(Car2).Start();//車輛2 _tollStation.Set();//開啓車閘,放行 Timer timer = new Timer(CloseDoor, null, 0, 2000);//2秒後關閉車閘 Console.ReadKey(); } static void Car1() { _tollStation.WaitOne();//等待開啓車閘,即_tollStation.Set(); Console.WriteLine("車輛1,順利經過。"); } static void Car2() { Thread.Sleep(3000);//睡眠3秒 _tollStation.WaitOne();//當醒來後車閘已經被關閉 Console.WriteLine("車輛2,順利經過。");//因此車輛2不會被經過 } /// <summary> /// 2秒後關閉車閘 /// </summary> static void CloseDoor(object o) { _tollStation.Reset();//關閉車閘 }
運行將打印:
車輛1,順利經過。
而車輛2將不會經過,由於當車輛2醒來時,車閘在2秒前已被關閉。