原文http://www.360doc.com/content/13/0829/14/4513754_310723961.shtml 一、作用 AutoResetEvent和ManualResetEvent可用於控制線程暫停或繼續,擁有重要的三個方法:WaitOne、Set和Reset。 這三個方法 ...
原文http://www.360doc.com/content/13/0829/14/4513754_310723961.shtml
一、作用
AutoResetEvent和ManualResetEvent可用於控制線程暫停或繼續,擁有重要的三個方法:WaitOne、Set和Reset。
這三個方法的官方定義並不好理解,什麼終止、非終止,亂七八糟的。在這裡,我們以一種通俗易懂的概念來說明。
二、比喻
如果把每個線程比作一輛汽車的話,AutoResetEvent和ManualResetEvent就是公路上的收費站。
其中:
Reset 關閉收費站車閘禁止通行(攔截車輛才好收費啊);
WaitOne 收費員等待下一輛車輛過來(然後收費);
Set 開啟收費站車閘放行(交錢了就讓過去)。
三、AutoResetEvent和ManualResetEvent的區別
既然AutoResetEvent和ManualResetEvent都是收費站,那麼它們之間有什麼不同之處嗎?
顧名思義,Auto即自動,Manual即手動,而Reset根據上面的比喻表示關閉車閘,也就是前者可自動關閉車閘,後者需手動關閉車閘。
自動關閉車閘:即一輛車交錢通過後,車閘會自動關閉,然後再等待下一輛車過來交費。即每輛車都要經過這麼幾個步驟:被阻 > 交費 > 通行 > 車閘關閉
手動關閉車閘:車閘打開後,車閘不會自動關閉,如果不手動關閉車閘(即調用ManualResetEvent.Reset()方法)的話,車輛會一輛接一輛地通過……
所以WaitOne收費操作取決於車閘是否關閉(Reset),如果車閘是開啟的,WaitOne的收費願望只能落空,收費站形同虛設。
四、AutoResetEvent和ManualResetEvent的初始狀態
通過設置AutoResetEvent和ManualResetEvent構造函數可初始化收費站車閘狀態:
new Auto/ManualResetEvent(false):車閘預設關閉;
new Auto/ManualResetEvent(true): 車閘預設開啟。
如果new Auto/ManualResetEvent(true),即車閘預設開啟的話,WaitOne沒任何意義,車輛該通過還通過。
看下麵代碼:
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("噫!車閘是開的,我過來了!"); }
運行將列印:
噫!車閘是開的,我過來了!
如果將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 AutoResetEvent _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秒前已被關閉。
七、總結
1、看起來,ManualResetEvent更加自由、開放。如果把AutoResetEvent看作是只能單人通過的獨木橋的話,那麼ManualResetEvent就像一座城門,一下子可以涌入千軍萬馬,當然你也可以隨時關閉城門,讓後面的人進不來。
2、AutoResetEvent.Set() = ManualResetEvent.Set() + ManualResetEvent.Reset();
3、如果共用資源僅允許一個線程單獨使用的情況下,可以選擇AutoResetEvent;如果共用資源允許多個線程同時使用,則可以選擇ManualResetEvent。
4、如果要控制多個線程暫停、繼續,可以選擇ManualResetEvent。