C#多線程系列之線程完成數(shù)
解決一個問題
假如,程序需要向一個 Web 發(fā)送 5 次請求,受網(wǎng)路波動影響,有一定幾率請求失敗。如果失敗了,就需要重試。
示例代碼如下:
class Program
{
private static int count = 0;
static void Main(string[] args)
{
for (int i = 0; i < 5; i++)
new Thread(HttpRequest).Start(); // 創(chuàng)建線程
// 用于不斷向另一個線程發(fā)送信號
while (count < 5)
{
Thread.Sleep(100);
}
Console.WriteLine("任務(wù)執(zhí)行完畢");
}
// 模擬網(wǎng)絡(luò)請求
public static void HttpRequest()
{
Console.WriteLine("開始一個任務(wù)");
// 隨機生成一個數(shù),如果為偶數(shù),則模擬請求失敗
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;
}
// 完成一次任務(wù),+1
Interlocked.Add(ref count,1);
Console.WriteLine($"完成任務(wù),count={count}");
}
}代碼太糟糕了,但我們可以使用 CountdownEvent 類來改造它。
CountdownEvent 類
表示在計數(shù)變?yōu)榱銜r處于有信號狀態(tài)的同步基元。
也就是說,設(shè)定一個計數(shù)器,每個線程完成后,就會減去 1 ,當(dāng)計數(shù)器為 0 時,代表所有線程都已經(jīng)完成了任務(wù)。
構(gòu)造函數(shù)和方法
CountdownEvent 類的構(gòu)造函數(shù)如下:
| 構(gòu)造函數(shù) | 說明 |
|---|---|
| CountdownEvent(Int32) | 使用指定計數(shù)初始化 CountdownEvent 類的新實例。 |
CountdownEvent 類的常用方法如下:
| 方法 | 說明 |
|---|---|
| AddCount() | 將 CountdownEvent 的當(dāng)前計數(shù)加 1。 |
| AddCount(Int32) | 將 CountdownEvent 的當(dāng)前計數(shù)增加指定值。 |
| Reset() | 將 CurrentCount 重置為 InitialCount 的值。 |
| Reset(Int32) | 將 InitialCount 屬性重新設(shè)置為指定值。 |
| Signal() | 向 CountdownEvent 注冊信號,同時減小 CurrentCount 的值。 |
| Signal(Int32) | 向 CountdownEvent 注冊多個信號,同時將 CurrentCount 的值減少指定數(shù)量。 |
| TryAddCount() | 增加一個 CurrentCount 的嘗試。 |
| TryAddCount(Int32) | 增加指定值的 CurrentCount 的嘗試。 |
| Wait() | 阻止當(dāng)前線程,直到設(shè)置了 CountdownEvent 為止。 |
| Wait(CancellationToken) | 阻止當(dāng)前線程,直到設(shè)置了 CountdownEvent 為止,同時觀察 CancellationToken。 |
| Wait(Int32) | 阻止當(dāng)前線程,直到設(shè)置了 CountdownEvent 為止,同時使用 32 位帶符號整數(shù)測量超時。 |
| Wait(Int32, CancellationToken) | 阻止當(dāng)前線程,直到設(shè)置了 CountdownEvent 為止,并使用 32 位帶符號整數(shù)測量超時,同時觀察 CancellationToken。 |
| Wait(TimeSpan) | 阻止當(dāng)前線程,直到設(shè)置了 CountdownEvent 為止,同時使用 TimeSpan 測量超時。 |
| Wait(TimeSpan, CancellationToken) | 阻止當(dāng)前線程,直到設(shè)置了 CountdownEvent 為止,并使用 TimeSpan 測量超時,同時觀察 CancellationToken。 |
API 比較多,沒事,我們來慢慢了解它。
示例
我們來編寫一個場景代碼,一個有五件事,需要完成,分別派出 5 個人去實現(xiàn)。
.Wait(); 用在一個線程中,這個線程將等待其它完成都完成任務(wù)后,才能繼續(xù)往下執(zhí)行。
Signal(); 用于工作線程中,向 CountdownEvent 對象發(fā)送信號,告知線程已經(jīng)完成任務(wù),然后 CountdownEvent.CurrentCount 將減去 1。
當(dāng)計數(shù)器為 0 時,阻塞的線程將恢復(fù)執(zhí)行。
代碼示例如下:
class Program
{
// 手頭上有 5 件事
private static CountdownEvent countd = new CountdownEvent(5);
static void Main(string[] args)
{
Console.WriteLine("開始交待任務(wù)");
// 同時叫 5 個人,去做 5 件事
for (int i = 0; i < 5; i++)
{
Thread thread = new Thread(DoOne);
thread.Name = $"{i}";
thread.Start();
}
// 等他們都完成事情
countd.Wait();
Console.WriteLine("任務(wù)完成,線程退出");
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}完成一件事了");
}
}示例很簡單,每個線程在完成自己的任務(wù)時,需要調(diào)用 Signal() 方法,使得計數(shù)器減去1。
.Wait(); 可以等待所有的任務(wù)完成。
需要注意的是,如果不調(diào)用 Signal() 或者計數(shù)器一直不為0,那么 Wait() 將無限等待。
當(dāng)然,Wait() 可以設(shè)置等待時間,
另外我們也看到了常用方法中有 AddCount()、Reset()等。
這個類的等待控制方式比較寬松,Wait() 后,到底什么時候才能執(zhí)行,全憑其它線程自覺。
如果發(fā)現(xiàn)線程執(zhí)行任務(wù)失敗,我們可以不調(diào)用 Signal() 或者 使用 AddCount() 來增加次數(shù),進行重試
到此這篇關(guān)于C#多線程系列之線程完成數(shù)的文章就介紹到這了。希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
VsCode使用EmmyLua插件調(diào)試Unity工程Lua代碼的詳細步驟
這篇文章主要介紹了VsCode使用EmmyLua插件調(diào)試Unity工程Lua代碼,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-08-08
C#中Request.Cookies 和 Response.Cookies 的區(qū)別分析
本文通過實例代碼向我們展示了C#中Request.Cookies 和 Response.Cookies 的區(qū)別,文章淺顯易懂,這里推薦給大家。2014-11-11
c#判斷網(wǎng)絡(luò)連接狀態(tài)的示例分享
這篇文章主要介紹了使用c#判斷網(wǎng)絡(luò)連接狀態(tài)的示例,需要的朋友可以參考下2014-02-02

