AutoResetEvent和ManualResetEvent(多線程操做)

摘自風中靈藥的博客:https://www.cnblogs.com/qingyun163/archive/2013/01/05/2846633.html#!commentshtml

AutoResetEvent和ManualResetEvent可用於控制線程暫停或繼續,擁有重要的三個方法:WaitOneSetReset函數

 

若是把每一個線程比做一輛汽車的話,AutoResetEventManualResetEvent就是公路上的收費站。spa

其中:線程

Reset 關閉收費站車閘禁止通行(攔截車輛纔好收費啊);code

WaitOne 收費員等待下一輛車輛過來(而後收費);htm

Set    開啓收費站車閘放行(交錢了就讓過去)。blog

 

AutoResetEvent和ManualResetEvent的區別隊列

既然AutoResetEventManualResetEvent都是收費站,那麼它們之間有什麼不一樣之處嗎?get

顧名思義,Auto即自動,Manual即手動,而Reset根據上面的比喻表示關閉車閘,也就是前者可自動關閉車閘,後者需手動關閉車閘。博客

自動關閉車閘:即一輛車交錢經過後,車閘會自動關閉,而後再等待下一輛車過來交費。即每輛車都要通過這麼幾個步驟:被阻 > 交費 > 通行 > 車閘關閉

手動關閉車閘:車閘打開後,車閘不會自動關閉,若是不手動關閉車閘(即調用ManualResetEvent.Reset()方法)的話,車輛會一輛接一輛地經過……

 

AutoResetEvent和ManualResetEvent的初始狀態

經過設置AutoResetEventManualResetEvent構造函數可初始化收費站車閘狀態:

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秒前已被關閉。

相關文章
相關標籤/搜索