首頁 > 軟體

C#多執行緒系列之執行緒完成數

2022-02-13 19:00:15

解決一個問題

假如,程式需要向一個 Web 傳送 5 次請求,受網路波動影響,有一定機率請求失敗。如果失敗了,就需要重試。

範例程式碼如下:

    class Program
    {
        private static int count = 0;
        static void Main(string[] args)
        {
            for (int i = 0; i < 5; i++)
                new Thread(HttpRequest).Start();            // 建立執行緒

            // 用於不斷向另一個執行緒傳送訊號
            while (count < 5)
            {
                Thread.Sleep(100);
            }
            Console.WriteLine("任務執行完畢");
        }


        // 模擬網路請求
        public static void HttpRequest()
        {
            Console.WriteLine("開始一個任務");
            // 隨機生成一個數,如果為偶數,則模擬請求失敗
            bool isSuccess = (new Random().Next(0, 10)) % 2 == 0;

            // ... ...模擬請求 HTTP
            Thread.Sleep(TimeSpan.FromSeconds(2));

            // 請求失敗則重試
            if (!isSuccess)
            {
                Console.WriteLine($"請求失敗,count={count}");
                new Thread(() =>
                {
                    HttpRequest();
                }).Start();
                return;
            }
            // 完成一次任務,+1
            Interlocked.Add(ref count,1);
            Console.WriteLine($"完成任務,count={count}");
        }
    }

程式碼太糟糕了,但我們可以使用 CountdownEvent 類來改造它。

CountdownEvent 類

表示在計數變為零時處於有訊號狀態的同步基元。

也就是說,設定一個計數器,每個執行緒完成後,就會減去 1 ,當計數器為 0 時,代表所有執行緒都已經完成了任務。

建構函式和方法

CountdownEvent 類別建構函式如下:

建構函式說明
CountdownEvent(Int32)使用指定計數初始化 CountdownEvent 類的新範例。

CountdownEvent 類的常用方法如下:

方法說明
AddCount()將 CountdownEvent 的當前計數加 1。
AddCount(Int32)將 CountdownEvent 的當前計數增加指定值。
Reset()將 CurrentCount 重置為 InitialCount 的值。
Reset(Int32)將 InitialCount 屬性重新設定為指定值。
Signal()向 CountdownEvent 註冊訊號,同時減小 CurrentCount 的值。
Signal(Int32)向 CountdownEvent 註冊多個訊號,同時將 CurrentCount 的值減少指定數量。
TryAddCount()增加一個 CurrentCount 的嘗試。
TryAddCount(Int32)增加指定值的 CurrentCount 的嘗試。
Wait()阻止當前執行緒,直到設定了 CountdownEvent 為止。
Wait(CancellationToken)阻止當前執行緒,直到設定了 CountdownEvent 為止,同時觀察 CancellationToken。
Wait(Int32)阻止當前執行緒,直到設定了 CountdownEvent 為止,同時使用 32 位帶符號整數測量超時。
Wait(Int32, CancellationToken)阻止當前執行緒,直到設定了 CountdownEvent 為止,並使用 32 位帶符號整數測量超時,同時觀察 CancellationToken。
Wait(TimeSpan)阻止當前執行緒,直到設定了 CountdownEvent 為止,同時使用 TimeSpan 測量超時。
Wait(TimeSpan, CancellationToken)阻止當前執行緒,直到設定了 CountdownEvent 為止,並使用 TimeSpan 測量超時,同時觀察 CancellationToken。

API 比較多,沒事,我們來慢慢了解它。

範例

我們來編寫一個場景程式碼,一個有五件事,需要完成,分別派出 5 個人去實現。

.Wait(); 用在一個執行緒中,這個執行緒將等待其它完成都完成任務後,才能繼續往下執行。

Signal(); 用於工作執行緒中,向 CountdownEvent 物件傳送訊號,告知執行緒已經完成任務,然後 CountdownEvent.CurrentCount 將減去 1。

當計數器為 0 時,阻塞的執行緒將恢復執行。

程式碼範例如下:

    class Program
    {
        // 手頭上有 5 件事
        private static CountdownEvent countd = new CountdownEvent(5);
        static void Main(string[] args)
        {
            Console.WriteLine("開始交待任務");
            // 同時叫 5 個人,去做 5 件事
            for (int i = 0; i < 5; i++)
            {
                Thread thread = new Thread(DoOne);
                thread.Name = $"{i}";
                thread.Start();
            }


            // 等他們都完成事情
            countd.Wait();

            Console.WriteLine("任務完成,執行緒退出");
            Console.ReadKey();
        }

        public static void DoOne()
        {
            int n = new Random().Next(0, 10);
            // 模擬要 n 秒才能完成
            Thread.Sleep(TimeSpan.FromSeconds(n));
            // 完成了,減去一件事
            countd.Signal();
            Console.WriteLine($"    {Thread.CurrentThread.Name}完成一件事了");
        }
    }

範例很簡單,每個執行緒在完成自己的任務時,需要呼叫 Signal() 方法,使得計數器減去1。

.Wait(); 可以等待所有的任務完成。

需要注意的是,如果不呼叫 Signal() 或者計數器一直不為0,那麼 Wait() 將無限等待。

當然,Wait() 可以設定等待時間,

另外我們也看到了常用方法中有 AddCount()Reset()等。

這個類的等待控制方式比較寬鬆,Wait() 後,到底什麼時候才能執行,全憑其它執行緒自覺。

如果發現執行緒執行任務失敗,我們可以不呼叫 Signal() 或者 使用 AddCount() 來增加次數,進行重試

到此這篇關於C#多執行緒系列之執行緒完成數的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com