個人對AutoResetEvent和ManualResetEvent的理解


僅個人見解,不對之處請指正,謝謝。

 

一、作用

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

這三個方法的官方定義並不好理解,什么終止、非終止,亂七八糟的。在這里,我們以一種通俗易懂的概念來說明。

 

二、比喻

如果把每個線程比作高速公路的一個車道的話,AutoResetEventManualResetEvent就是高速公路上的收費站。

其中:

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

WaitOne 收費員等待下一輛車輛過來(然后收費);

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

 

三、AutoResetEvent和ManualResetEvent的區別

既然AutoResetEventManualResetEvent都是收費站,那么它們之間有什么不同之處嗎?

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

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

手動關閉車閘:車閘打開后,車閘不會自動關閉,如果不手動關閉車閘(即調用ManualResetEvent.Reset()方法)的話,車輛會一輛接一輛地通過……

 

所以WaitOne收費操作取決於車閘是否關閉(Reset),如果車閘是開啟的,WaitOne的收費願望只能落空,收費站形同虛設。

 

四、AutoResetEvent和ManualResetEvent的初始狀態

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

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

 

七、總結

1、看起來,ManualResetEvent更加自由、開放。如果把AutoResetEvent看作是只能單人通過的獨木橋的話,那么ManualResetEvent就像一座城門,一下子可以涌入千軍萬馬,當然你也可以隨時關閉城門,讓后面的人進不來。

2、AutoResetEvent.Set()ManualResetEvent.Set()ManualResetEvent.Reset();

3、如果共享資源僅允許一個線程單獨使用的情況下,可以選擇AutoResetEvent;如果共享資源允許多個線程同時使用,則可以選擇ManualResetEvent

4、如果要控制多個線程暫停、繼續,可以選擇ManualResetEvent

5、等您來補充……


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM