[英]How to wait on Async tasks
static void Main(string[] args)
{
Action myAction = async () =>
{
await Task.Delay(5);
Console.WriteLine(Interlocked.Add(ref ExecutionCounter, 1));
};
var actions = new[] { myAction, myAction, myAction };
Task.WaitAll(actions.Select(a => Execute(a)).ToArray()); //This blocks, right?
Console.WriteLine("Done waiting on tasks.");
Console.ReadLine();
}
static int ExecutionCounter = 0;
private static Task Execute(Action a)
{
return Task.Factory.StartNew(async () =>
{
await Task.Delay(5);
a();
});
}
這看起來很簡單,但是自然地,輸出總是看起來像這樣(數字的順序當然會改變):
等待任務完成。
2
1個
3
我在這里想念什么? 為什么Task.WaitAll
不會像我期望的Task.WaitAll
阻塞?
因此,這里有幾個單獨的錯誤。
首先,對於Execute
,您將StartNew
與async
lambda一起使用。 由於StartNew
沒有像Task.Run
這樣的Task<Task>
返回過載, Task.Run
您有一個返回Task
的方法,該方法指示異步操作何時完成啟動 ,而不是異步操作何時完成,這意味着Execute
返回的Task
將立即完成,而不是在Delay
完成或您調用的操作完成之后。 此外,運行異步方法時根本沒有理由完全使用StartNew
或Run
,您可以正常執行它們並await
它們,而無需將它們推送到線程池線程中。
接下來, Execute
接受一個Action
,這意味着它是一個同步方法,不計算任何值。 您提供的是一個異步方法,但是由於委托沒有返回Task
,因此Execute
無法await
它。 如果希望Execute
處理異步方法,則需要接受返回Task
的委托。
因此,鑒於所有Execute
應該看起來像這樣。
private static async Task Execute(Func<Task> action)
{
await Task.Delay(TimeSpan.FromMilliseconds(5));
await action();
}
接下來進入Main
方法。 如前所述,當您嘗試提供async
方法時, Execute
接受Action
。 這意味着在運行操作時,代碼將在您的操作完成之前繼續執行。 您需要將其調整為使用Task
返回方法。
所有這一切后,你的代碼仍然有它的競爭條件,在概念層面,這將阻止你獲得理論上的結果以正確的順序。 您正在並行執行3個不同的操作,因此,它們可以按任何順序完成。 在原子地遞增計數器的同時,有可能一個線程輸出一個計數器,然后運行另一個線程,遞增計數器,打印其值,然后讓另一個線程再次運行並打印出該值,這可能是因為您所擁有的,即使修復了上述所有錯誤之后也是如此。 為了確保按順序打印值,您需要確保增量和控制台寫入是原子執行的 。
現在,您可以像這樣寫出Main
方法:
int ExecutionCounter = 0;
object key = new object();
Func<Task> myAction = async () =>
{
await Task.Delay(TimeSpan.FromMilliseconds(5));
lock (key)
{
Console.WriteLine(++ExecutionCounter);
}
};
var actions = new[] { myAction, myAction, myAction };
Task.WaitAll(actions.Select(a => Execute(a)).ToArray()); //This blocks, right?
是的,正如您的評論所提到的,調用WaitAll
將阻止,而不是異步。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.