[英]How to have mutliple threads await a single Task?
我讀過這個: 從多個線程等待相同的任務是否可以 - 等待線程安全嗎? 我不清楚答案,所以這是一個特定的用例。
我有一個執行一些異步網絡I / O的方法。 多個線程可以同時觸發此方法,我不希望它們全部調用網絡請求,如果請求已在進行中我想阻止/等待第二個+線程,並且一旦單個IO操作就讓它們全部恢復已經完成了。
我應該怎么寫下面的偽代碼? 我猜每個調用線程確實需要獲得自己的Task
,所以每個人都可以得到它自己的延續,所以不是返回currentTask
我應該回到一個新的Task
是由“內”完成Task
從DoAsyncNetworkIO
。 有沒有干凈的方法來做到這一點,還是我必須手動滾動它?
static object mutex = new object();
static Task currentTask;
async Task Fetch()
{
lock(mutex)
{
if(currentTask != null)
return currentTask;
}
currentTask = DoAsyncNetworkIO();
await currentTask;
lock(mutex)
{
var task = currentTask;
currentTask = null;
return task;
}
}
您可以使用SemaphoreSlim
來確保只有一個線程實際執行后台線程。
假設您的基本任務(實際執行IO的那個)在一個名為baseTask()
的方法中,我將仿效:
static async Task baseTask()
{
Console.WriteLine("Starting long method.");
await Task.Delay(1000);
Console.WriteLine("Finished long method.");
}
然后你可以像這樣初始化一個SemaphoreSlim
,有點像AutoResetEvent
,初始狀態設置為true
:
static readonly SemaphoreSlim signal = new SemaphoreSlim(1, 1);
然后在一個方法中調用baseTask()
,該方法檢查signal
,看看這是否是第一個嘗試運行baseTask()
線程,如下所示:
static async Task<bool> taskWrapper()
{
bool firstIn = await signal.WaitAsync(0);
if (firstIn)
{
await baseTask();
signal.Release();
}
else
{
await signal.WaitAsync();
signal.Release();
}
return firstIn;
}
然后你的多個線程將等待taskWrapper()
而不是直接等待baseTask()
。
將它完全放在可編輯的控制台應用程序中:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Demo
{
static class Program
{
static void Main()
{
for (int it = 0; it < 10; ++it)
{
Console.WriteLine($"\nStarting iteration {it}");
Task[] tasks = new Task[5];
for (int i = 0; i < 5; ++i)
tasks[i] = Task.Run(demoTask);
Task.WaitAll(tasks);
}
Console.WriteLine("\nFinished");
Console.ReadLine();
}
static async Task demoTask()
{
int id = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine($"Thread {id} starting");
bool firstIn = await taskWrapper();
Console.WriteLine($"Task {id}: executed: {firstIn}");
}
static async Task<bool> taskWrapper()
{
bool firstIn = await signal.WaitAsync(0);
if (firstIn)
{
await baseTask();
signal.Release();
}
else
{
await signal.WaitAsync();
signal.Release();
}
return firstIn;
}
static async Task baseTask()
{
Console.WriteLine("Starting long method.");
await Task.Delay(1000);
Console.WriteLine("Finished long method.");
}
static readonly SemaphoreSlim signal = new SemaphoreSlim(1, 1);
}
}
(這些方法都是靜態的,因為它們位於控制台應用程序中;在實際代碼中,它們是非靜態方法。)
await
根本不一定使用continuation( Task.ContinueWith
kind)。 即使它確實如此,您也可以在一個Task
上擁有多個延續 - 它們不能全部同步運行(如果您有同步上下文,則可能會遇到一些問題)。
請注意,您的偽代碼不是線程安全的 - 您不能只執行currentTask = DoAsyncNetworkIO();
在鎖外。 只有await
本身是線程安全的,即便如此,只是因為你正在等待的Task
類以線程安全的方式實現await
契約。 任何人都可以寫自己的等待/等待,所以一定要注意:)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.