天天看點

C#多線程(6):線程通知

C#多線程(6):線程通知

目錄

AutoRestEvent 類

常用方法

一個簡單的示例

解釋一下

複雜一點的示例

解釋

回顧一下,前面 lock、Monitor 部分我們學習了線程鎖,Mutex 部分學習了程序同步,Semaphor 部分學習了資源池限制。

這一篇将學習 C# 中用于發送線程通知的 AutoRestEvent 類。

用于從一個線程向另一個線程發送通知。

微軟文檔是這樣介紹的:表示線程同步事件在一個等待線程釋放後收到信号時自動重置。

其構造函數隻有一個:

構造函數裡面的參數用于設定信号狀态。

構造函數 說明

AutoResetEvent(Boolean) 用一個訓示是否将初始狀态設定為終止的布爾值初始化 AutoResetEvent 類的新執行個體。

真糟糕的機器翻譯。

AutoRestEvent 類是幹嘛的,構造函數的參數又是幹嘛的?不着急,我們來先來看看這個類常用的方法:

方法 說明

Close() 釋放由目前 WaitHandle 占用的所有資源。

Reset() 将事件狀态設定為非終止,進而導緻線程受阻。

Set() 将事件狀态設定為有信号,進而允許一個或多個等待線程繼續執行。

WaitOne() 阻止目前線程,直到目前 WaitHandle 收到信号。

WaitOne(Int32) 阻止目前線程,直到目前 WaitHandle 收到信号,同時使用 32 位帶符号整數指定時間間隔(以毫秒為機關)。

WaitOne(Int32, Boolean) 阻止目前線程,直到目前的 WaitHandle 收到信号為止,同時使用 32 位帶符号整數指定時間間隔,并指定是否在等待之前退出同步域。

WaitOne(TimeSpan) 阻止目前線程,直到目前執行個體收到信号,同時使用 TimeSpan 指定時間間隔。

WaitOne(TimeSpan, Boolean) 阻止目前線程,直到目前執行個體收到信号為止,同時使用 TimeSpan 指定時間間隔,并指定是否在等待之前退出同步域。

這裡我們編寫一個這樣的程式:

建立一個線程,能夠執行多個階段的任務;每完成一個階段,都需要停下來,等待子線程發生通知,才能繼續下一步執行。

.WaitOne() 用來等待另一個線程發送通知;

.Set() 用來對線程發出通知,此時 AutoResetEvent 變成終止狀态;

.ReSet() 用來重置 AutoResetEvent 狀态;

class Program
{
    // 線程通知
    private static AutoResetEvent resetEvent = new AutoResetEvent(false);

    static void Main(string[] args)
    {
        // 建立線程
        new Thread(DoOne).Start();

        // 用于不斷向另一個線程發送信号
        while (true)
        {
            Console.ReadKey();
            resetEvent.Set();           // 發生通知,設定終止狀态
        }
    }

    public static void DoOne()
    {
        Console.WriteLine("等待中,請發出信号允許我運作");

        // 等待其它線程發送信号
        resetEvent.WaitOne();

        Console.WriteLine("\n     收到信号,繼續執行");
        for (int i = 0; i < 5; i++) Thread.Sleep(TimeSpan.FromSeconds(0.5));

        resetEvent.Reset(); // 重置為非終止狀态
        Console.WriteLine("\n第一階段運作完畢,請繼續給予訓示");

        // 等待其它線程發送信号
        resetEvent.WaitOne();
        Console.WriteLine("\n     收到信号,繼續執行");
        for (int i = 0; i < 5; i++) Thread.Sleep(TimeSpan.FromSeconds(0.5));

        Console.WriteLine("\n第二階段運作完畢,線程結束,請手動關閉視窗");
    }
}           

AutoResetEvent 對象有終止和非終止狀态。Set() 設定終止狀态,Reset() 重置非終止狀态。

這個終止狀态,可以了解成信号已經通知;非終止狀态則是信号還沒有通知。

注意,注意終止狀态和非終止狀态指的是 AutoResetEvent 的狀态,不是指線程的狀态。

線程通過調用 WaitOne() 方法,等待信号;

另一個線程可以調用 Set() 通知 AutoResetEvent 釋放等待線程。

然後 AutoResetEvent 變為終止狀态。

需要注意的是,如果 AutoResetEvent 已經處于終止狀态,那麼線程調用 WaitOne() 不會再起作用。除非調用Reset() 。

構造函數中的參數,正是設定這個狀态的。true 代表終止狀态,false 代表非終止狀态。如果使用 new AutoResetEvent(true); ,則線程一開始是無需等待信号的。

在使用完類型後,您應直接或間接釋放類型,顯式調用 Close()/Dispose() 或 使用 using。 當然,也可以直接退出程式。

需要注意的是,如果多次調用 Set() 的時間間隔過短,如果第一次 Set() 還沒有結束(信号發送需要處理時間),那麼第二次 Set() 可能無效(不起作用)。

我們設計一個程式:

Two 線程開始處于阻塞狀态;

線程 One 可以設定線程 Two 繼續運作,然後阻塞自己;

線程 Two 可以設定 One 繼續運作,然後阻塞自己;

程式代碼如下(運作後,請将鍵盤設定成英文輸入狀态再按下按鍵):

class Program
{
    // 控制第一個線程
    // 第一個線程開始時,AutoResetEvent 處于終止狀态,無需等待信号
    private static AutoResetEvent oneResetEvent = new AutoResetEvent(true);

    // 控制第二個線程
    // 第二個線程開始時,AutoResetEvent 處于非終止狀态,需要等待信号
    private static AutoResetEvent twoResetEvent = new AutoResetEvent(false);

    static void Main(string[] args)
    {
        new Thread(DoOne).Start();
        new Thread(DoTwo).Start();

        Console.ReadKey();
    }

    public static void DoOne()
    {
        while (true)
        {
            Console.WriteLine("\n① 按一下鍵,我就讓DoTwo運作");
            Console.ReadKey();
            twoResetEvent.Set();
            oneResetEvent.Reset();
            // 等待 DoTwo() 給我信号
            oneResetEvent.WaitOne();

            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine("\n     DoOne() 執行");
            Console.ForegroundColor = ConsoleColor.White;
        }
    }

    public static void DoTwo()
    {
        while (true)
        {
            Thread.Sleep(TimeSpan.FromSeconds(1));

            // 等待 DoOne() 給我信号
            twoResetEvent.WaitOne();

            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine("\n     DoTwo() 執行");
            Console.ForegroundColor = ConsoleColor.White;

            Console.WriteLine("\n② 按一下鍵,我就讓DoOne運作");
            Console.ReadKey();
            oneResetEvent.Set();
            twoResetEvent.Reset();
        }
    }
}
           

兩個線程具有的功能:阻塞自己、解除另一個線程的阻塞。

用電影《最佳拍檔》裡面的一個畫面來了解。

DoOne 、DoTwo 輪流呼吸,不能自己控制自己呼吸,但自己能夠決定别人呼吸。

你搞我,我搞你,就能互相呼吸了。

當然WaitOne() 也可以設定等待時間,如果 光頭佬(DoOne) 耍賴不讓 金剛(DoTwo)呼吸,金剛等待一定時間後,可以強行蕩動天平,落地呼吸。

注意,AutoRestEvent 用得不當容易發生死鎖。

另外 AutoRestEvent 使用的是核心時間模式,是以等待時間不能太長,不然比較耗費 CPU 時間。

AutoResetEvent 也适合用于線程同步。

另外,線程中使用 WaitOne() ,另一個線程使用 Set() 通知後, AutoResetEvent 對象會自動恢複非終止狀态,不需要線程使用 Reset() 。

原文位址

https://www.cnblogs.com/whuanle/p/12730169.html