C# 的 WhenAll 與 WaitAll 的差異在哪裡
當需要使用 Task 類別來建立出一群的 非同步工作 物件,使這些工作物件可以並行執行,在 C# 中,可以使用 Task.WaitAll() 方法或者使用 Task.WhenAll() 方法來等待這些工作的全部都完成。這兩種做法的第一個差異就是,Task.WaitAll() 使用封鎖當前執行緒,等待所有的非同步工作完成,而 Task.WhenAll() 可以使用 await 關鍵字來等待所有的非同步工作的完成,而在等待的時候,將會 return 到原先呼叫的方法內。
在這裡使用一個範例程式,建立起 15 個非同步工作,這些非同步工作將會使用 HttpClient 連線到 https://lobworkshop.azurewebsites.net/api/RemoteSource/Add/15/43 Web API 服務,要求將 15 / 43 相加起來,不過,最後一個參數則是指定這個兩個整數的相加計算需要暫緩多久的時間(以秒來計算),才會回傳結果回來。
在 Main() 方法內的 WaitAll() ,將會逐一把 sleepSeconds 的每個整數值取出來,並且組合成為一個新的 URL,使用 HttpClient 類別產生的物件,呼叫遠端的 Web API 服務。原則上,這些整數相加的工作都會指定在 1~7 秒鐘內完成。
所產生的 task 物件,將會儲存到 List> allTasks 變數內,最後,使用 Task.WaitAll(allTasks.ToArray()); 來進行執行緒封鎖的等待方式,等待所有的非同步工作執行完成。
而在在 Main() 方法內的 WhenAll() ,將會逐一把 sleepSeconds 的每個整數值取出來,並且組合成為一個新的 URL,使用 HttpClient 類別產生的物件,呼叫遠端的 Web API 服務。原則上,這些整數相加的工作都會指定在 1~7 秒鐘內完成。
所產生的 task 物件,將會儲存到 List> allTasks 變數內,最後,使用 await Task.WhenAll(allTasks.ToArray()); 來進行等待所有的非同步工作執行完成,底下是反覆執行三次的執行結果。
觀察這三次的執行結果可以得到 使用 Task.WhenAll() 方法所花費的時間小於使用 Task.WaitAll() 方法所花費的時間,不過,差距大約只有不到 0.5 秒
Wait total 7467 ms
Press any key for continuing...
Wait total 7147 ms
Wait total 7571 ms
Press any key for continuing...
Wait total 7139 ms
Wait total 7431 ms
Press any key for continuing...
Wait total 7138 ms
namespace WaitAllWhenAll
{
class Program
{
static List<int> sleepSeconds = new List<int>() { 3, 2, 5, 7, 2, 3, 4, 5, 5, 1, 7, 2, 4, 4, 5 };
static void Main(string[] args)
{
WaitAll();
WhenAll();
Console.WriteLine("Press any key for continuing...");
Console.ReadKey();
}
private static async void WhenAll()
{
string host = "https://lobworkshop.azurewebsites.net";
string path = "/api/RemoteSource/Add/15/43/@";
string url = $"{host}{path}";
Stopwatch sw = new Stopwatch();
sw.Start();
List<Task<string>> allTasks = new List<Task<string>>();
foreach (var item in sleepSeconds)
{
var fooUrl = url.Replace("@", item.ToString());
var task = new HttpClient().GetStringAsync(fooUrl);
allTasks.Add(task);
}
await Task.WhenAll(allTasks.ToArray());
sw.Stop();
Console.WriteLine($"Wait total {sw.ElapsedMilliseconds} ms");
}
private static void WaitAll()
{
string host = "https://lobworkshop.azurewebsites.net";
string path = "/api/RemoteSource/Add/15/43/@";
string url = $"{host}{path}";
Stopwatch sw = new Stopwatch();
sw.Start();
List<Task<string>> allTasks = new List<Task<string>>();
foreach (var item in sleepSeconds)
{
var fooUrl = url.Replace("@", item.ToString());
var task = new HttpClient().GetStringAsync(fooUrl);
allTasks.Add(task);
}
Task.WaitAll(allTasks.ToArray());
sw.Stop();
Console.WriteLine($"Wait total {sw.ElapsedMilliseconds} ms");
}
}
}