C#多線程系列之線程完成數
解決一個問題
假如,程序需要向一個 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#多線程系列之線程完成數的文章就介紹到這瞭。希望對大傢的學習有所幫助,也希望大傢多多支持WalkonNet。