C# Task.WhenAll的用法小結(jié)
一、簡介
C# 中的 Task.WhenAll 方法是一種用于并行執(zhí)行多個(gè)任務(wù)并等待它們?nèi)客瓿傻母咝Х绞?。它接收一個(gè)任務(wù)數(shù)組或任務(wù)集合作為參數(shù),并返回一個(gè)表示所有任務(wù)都已完成的新任務(wù)。當(dāng)調(diào)用 Task.WhenAll 返回的任務(wù)的 Wait 方法或 Result 屬性時(shí),程序會(huì)阻塞,直到所有傳入的任務(wù)都已完成。如果任何一個(gè)傳入的任務(wù)失敗并拋出異常,Task.WhenAll 返回的任務(wù)也會(huì)以異常結(jié)束,從而允許開發(fā)者集中處理錯(cuò)誤。這種方法在需要并行處理多個(gè)獨(dú)立任務(wù)并等待它們?nèi)客瓿蓵r(shí)非常有用,能夠顯著提高應(yīng)用程序的性能和響應(yīng)速度。
Task.WhenAll 和 Task.WaitAll 都是 C# 中用于處理并發(fā)任務(wù)的方法,但它們之間存在顯著的不同。以下是對兩者的具體比較:
異步與同步
- Task.WhenAll:是異步的,不會(huì)阻塞調(diào)用它的線程。它返回一個(gè)表示所有輸入任務(wù)聯(lián)合完成的新 Task 對象。
- Task.WaitAll:是同步的,會(huì)阻塞調(diào)用它的線程,直到所有任務(wù)都完成為止。它沒有返回值。
使用場景
- Task.WhenAll:適用于需要并行執(zhí)行多個(gè)任務(wù),并且不想阻塞當(dāng)前線程的場景。它允許在等待任務(wù)完成的同時(shí),執(zhí)行其他操作。
- Task.WaitAll:適用于需要等待所有任務(wù)都完成才能繼續(xù)執(zhí)行的場景,但它會(huì)阻塞當(dāng)前線程,因此不適合需要保持線程響應(yīng)性的應(yīng)用。
異常處理
- Task.WhenAll:當(dāng)任何一個(gè)輸入任務(wù)拋出異常時(shí),它會(huì)立即返回一個(gè)處于出錯(cuò)狀態(tài)的 Task 對象。開發(fā)者可以通過檢查返回的 Task 對象來處理異常。
- Task.WaitAll:會(huì)一直等待所有任務(wù)完成,包括在其中一個(gè)任務(wù)拋出異常時(shí)。異常會(huì)被捕獲并包裝在 AggregateException 中,需要開發(fā)者處理這個(gè)異常。
返回值與結(jié)果獲取
- Task.WhenAll:返回一個(gè)新的 Task 對象,該對象的 Result 屬性是一個(gè)包含所有輸入任務(wù)結(jié)果的數(shù)組(如果輸入任務(wù)有返回值)。如果輸入任務(wù)沒有返回值,結(jié)果數(shù)組的元素類型為 void。
- Task.WaitAll:沒有返回值,它僅用于等待所有任務(wù)完成。
官方的文檔:
官方的解釋:
創(chuàng)建一個(gè)任務(wù),該任務(wù)將在所有提供的任務(wù)完成時(shí)完成。
主要接口:
| WhenAll(IEnumerable<Task>) | 創(chuàng)建一個(gè)任務(wù),該任務(wù)將在可枚舉集合中的所有 Task 對象完成時(shí)完成 |
| WhenAll(ReadOnlySpan<Task>) | 創(chuàng)建一個(gè)任務(wù),該任務(wù)將在所有提供的任務(wù)完成時(shí)完成 |
| WhenAll(Task[]) | 創(chuàng)建一個(gè)任務(wù),該任務(wù)將在數(shù)組中的所有 Task 對象完成時(shí)完成 |
| WhenAll<TResult>(ReadOnlySpan<Task<TResult>>) | 創(chuàng)建一個(gè)任務(wù),該任務(wù)將在所有提供的任務(wù)完成時(shí)完成 |
| WhenAll<TResult>(IEnumerable<Task<TResult>>) | 創(chuàng)建一個(gè)任務(wù),該任務(wù)將在可枚舉集合中的所有 Task<TResult> 對象完成時(shí)完成 |
| WhenAll<TResult>(Task<TResult>[]) | 創(chuàng)建一個(gè)任務(wù),該任務(wù)將在數(shù)組中的所有 Task<TResult> 對象完成時(shí)完成 |
WhenAll(IEnumerable<Task>)
創(chuàng)建一個(gè)任務(wù),該任務(wù)將在可枚舉集合中的所有 Task 對象完成時(shí)完成。
public static System.Threading.Tasks.Task WhenAll (System.Collections.Generic.IEnumerable<System.Threading.Tasks.Task> tasks);
參數(shù)
tasks IEnumerable<Task>
要等待完成的任務(wù)。
返回
Task
表示所有提供任務(wù)的完成的任務(wù)。
例外
ArgumentNullException
tasks 參數(shù) null。
ArgumentException
tasks 集合包含 null 任務(wù)。
注解
當(dāng)對一組任務(wù)的狀態(tài)或一組任務(wù)引發(fā)的異常感興趣時(shí),通常會(huì)調(diào)用返回 Task 對象的 WhenAll 方法的重載。
備注 對 WhenAll(IEnumerable<Task>) 方法的調(diào)用不會(huì)阻止調(diào)用線程。
如果提供的任何任務(wù)都以錯(cuò)誤狀態(tài)完成,則返回的任務(wù)也將處于 Faulted 狀態(tài),其中其異常將包含每個(gè)提供的任務(wù)的未包裝異常集的聚合。
如果提供的任務(wù)均未出錯(cuò),但其中至少一個(gè)任務(wù)已取消,則返回的任務(wù)將以 Canceled 狀態(tài)結(jié)束。
如果沒有任何任務(wù)出錯(cuò)且未取消任何任務(wù),則生成的任務(wù)將以 RanToCompletion 狀態(tài)結(jié)束。
如果提供的數(shù)組/可枚舉不包含任何任務(wù),則返回的任務(wù)將在返回給調(diào)用方之前立即轉(zhuǎn)換為 RanToCompletion 狀態(tài)。
案例
新建一個(gè) winform 項(xiàng)目,建 winform 項(xiàng)目主要是看 Task.WhenAll 執(zhí)行過程中,是否會(huì)卡住 UI 線程,界面暫時(shí)沒添加任何控件。
代碼:
添加一個(gè)類 PingTool
using System;
using System.Net.Http;
using System.Threading.Tasks;
internal class PingTool
{
public async Task<bool> Ping(string url)
{
if (string.IsNullOrEmpty(url))
return false;
try
{
using (HttpClient client = new HttpClient())
{
client.Timeout = TimeSpan.FromSeconds(5);
var response = await client.GetAsync(url);
return response.IsSuccessStatusCode;
}
}
catch (System.Exception)
{
return false;
}
}
}Form1代碼:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WhenAllTest2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Start();
}
private async void Start()
{
List<Task<(string, bool)>> taskList = new List<Task<(string, bool)>>();
taskList.Add(PingTest("https://www.csdn.net/"));
taskList.Add(PingTest("https://www.zhihu.com/"));
taskList.Add(PingTest("https://www.microsoft.com/zh-cn/"));
taskList.Add(PingTest("https://www.baidu.com/"));
taskList.Add(PingTest("https://github.com/"));
var resultList = await Task.WhenAll(taskList);
foreach (var result in resultList)
{
Console.WriteLine("ping {0} 的結(jié)果:{1}", result.Item1, result.Item2);
}
Console.WriteLine("執(zhí)行完成");
}
private async Task<(string, bool)> PingTest(string ip)
{
PingTool tool = new PingTool();
bool result = await tool.Ping(ip);
return (ip, result);
}
}
}結(jié)果:

在執(zhí)行的過程中,拖動(dòng)界面可以看到,幾乎沒有什么影響。
WhenAll(ReadOnlySpan<Task>)
創(chuàng)建一個(gè)任務(wù),該任務(wù)將在所有提供的任務(wù)完成時(shí)完成。
public static System.Threading.Tasks.Task WhenAll (scoped ReadOnlySpan<System.Threading.Tasks.Task> tasks);
參數(shù)
tasks ReadOnlySpan<Task>
要等待完成的任務(wù)。
返回
Task
表示所有提供任務(wù)的完成的任務(wù)。
例外
ArgumentException
tasks 數(shù)組包含 null 任務(wù)。
注解
如果提供的任何任務(wù)都以錯(cuò)誤狀態(tài)完成,則返回的任務(wù)也將處于“出錯(cuò)”狀態(tài),其中異常將包含每個(gè)提供任務(wù)中未包裝的異常集的聚合。
如果提供的任務(wù)均未出錯(cuò),但至少其中一個(gè)任務(wù)已取消,則返回的任務(wù)將以“已取消”狀態(tài)結(jié)束。
如果沒有任何任務(wù)出錯(cuò)且未取消任何任務(wù),則生成的任務(wù)將以 RanToCompletion 狀態(tài)結(jié)束。
如果提供的跨度不包含任何任務(wù),則返回的任務(wù)將在返回給調(diào)用方之前立即轉(zhuǎn)換為 RanToCompletion 狀態(tài)。
在 .NET Framework 中,ReadOnlySpan 并不支持

這里我使用 .Net9 做了一個(gè)案例

代碼:
namespace WhenAllTest3
{
internal class Program
{
static async Task Main(string[] args)
{
// 定義多個(gè)異步任務(wù)
Task task1 = SimulateTaskAsync("任務(wù)1", 2000);
Task task2 = SimulateTaskAsync("任務(wù)2", 1000);
Task task3 = SimulateTaskAsync("任務(wù)3", 3000);
// 將任務(wù)放入一個(gè)數(shù)組
Task[] tasks = new[] { task1, task2, task3 };
// 使用 ReadOnlySpan<Task>
ReadOnlySpan<Task> taskSpan = new ReadOnlySpan<Task>(tasks);
Console.WriteLine("等待所有任務(wù)完成...");
// 等待所有任務(wù)完成
await Task.WhenAll(taskSpan);
Console.WriteLine("所有任務(wù)已完成。");
Console.ReadKey();
}
// 模擬異步任務(wù)
static async Task SimulateTaskAsync(string taskName, int delay)
{
Console.WriteLine($"{taskName} 開始執(zhí)行。");
await Task.Delay(delay);
Console.WriteLine($"{taskName} 執(zhí)行完成。");
}
}
}
運(yùn)行:

WhenAll(Task[])
創(chuàng)建一個(gè)任務(wù),該任務(wù)將在數(shù)組中的所有 Task 對象完成時(shí)完成。
public static System.Threading.Tasks.Task WhenAll (params System.Threading.Tasks.Task[] tasks);
參數(shù)
tasks Task[]
要等待完成的任務(wù)。
返回
Task
表示所有提供任務(wù)的完成的任務(wù)。
例外
ArgumentNullException
tasks 參數(shù) null。
ArgumentException
tasks 數(shù)組包含 null 任務(wù)。
注解
當(dāng)對一組任務(wù)的狀態(tài)或一組任務(wù)引發(fā)的異常感興趣時(shí),通常會(huì)調(diào)用返回 Task 對象的 WhenAll 方法的重載。
備注 對 WhenAll(Task[]) 方法的調(diào)用不會(huì)阻止調(diào)用線程。
如果提供的任何任務(wù)都以錯(cuò)誤狀態(tài)完成,則返回的任務(wù)也將處于 Faulted 狀態(tài),其中其異常將包含每個(gè)提供的任務(wù)的未包裝異常集的聚合。
如果提供的任務(wù)均未出錯(cuò),但其中至少一個(gè)任務(wù)已取消,則返回的任務(wù)將以 Canceled 狀態(tài)結(jié)束。
如果沒有任何任務(wù)出錯(cuò)且未取消任何任務(wù),則生成的任務(wù)將以 RanToCompletion 狀態(tài)結(jié)束。
如果提供的數(shù)組/可枚舉不包含任何任務(wù),則返回的任務(wù)將在返回給調(diào)用方之前立即轉(zhuǎn)換為 RanToCompletion 狀態(tài)。
代碼:
namespace WhenAllTest3
{
internal class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("開始執(zhí)行任務(wù)...");
// 定義多個(gè)異步任務(wù)
Task task1 = SimulateTaskAsync("任務(wù)1", 2000);
Task task2 = SimulateTaskAsync("任務(wù)2", 1000);
Task task3 = SimulateTaskAsync("任務(wù)3", 3000);
// 將任務(wù)放入一個(gè)數(shù)組
Task[] tasks = { task1, task2, task3 };
// 使用 Task.WhenAll 等待所有任務(wù)完成
await Task.WhenAll(tasks);
Console.WriteLine("所有任務(wù)已完成!");
}
// 模擬異步任務(wù)
static async Task SimulateTaskAsync(string taskName, int delay)
{
Console.WriteLine($"{taskName} 開始執(zhí)行。");
await Task.Delay(delay);
Console.WriteLine($"{taskName} 執(zhí)行完成。");
}
}
}運(yùn)行:

WhenAll<TResult>(ReadOnlySpan<Task<TResult>>)
創(chuàng)建一個(gè)任務(wù),該任務(wù)將在所有提供的任務(wù)完成時(shí)完成。
public static System.Threading.Tasks.Task<TResult[]> WhenAll<TResult> (scoped ReadOnlySpan<System.Threading.Tasks.Task<TResult>> tasks);
類型參數(shù)
TResult
參數(shù)
tasks ReadOnlySpan<Task<TResult>>
要等待完成的任務(wù)。
返回
Task<TResult[]>
表示所有提供任務(wù)的完成的任務(wù)。
例外
ArgumentException
tasks 數(shù)組包含 null 任務(wù)。
注解
如果提供的任何任務(wù)都以錯(cuò)誤狀態(tài)完成,則返回的任務(wù)也將處于“出錯(cuò)”狀態(tài),其中異常將包含每個(gè)提供任務(wù)中未包裝的異常集的聚合。
如果提供的任務(wù)均未出錯(cuò),但至少其中一個(gè)任務(wù)已取消,則返回的任務(wù)將以“已取消”狀態(tài)結(jié)束。
如果沒有任何任務(wù)出錯(cuò)且未取消任何任務(wù),則生成的任務(wù)將以 RanToCompletion 狀態(tài)結(jié)束。 返回的任務(wù)的結(jié)果將設(shè)置為一個(gè)數(shù)組,該數(shù)組包含所提供的任務(wù)的所有結(jié)果,其順序與提供的順序相同(例如,如果輸入任務(wù)數(shù)組包含 t1、t2、t3,則輸出任務(wù)的結(jié)果將返回一個(gè) TResult[],其中 arr[0] == t1)。Result, arr[1] == t2。結(jié)果和 arr[2] == t3。結(jié)果)。
如果提供的數(shù)組/可枚舉不包含任何任務(wù),則返回的任務(wù)將在返回給調(diào)用方之前立即轉(zhuǎn)換為 RanToCompletion 狀態(tài)。 返回的 TResult[] 將是 0 個(gè)元素的數(shù)組。
在 .NET Framework 中,ReadOnlySpan 并不支持
代碼:
namespace WhenAllTest3
{
internal class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("開始執(zhí)行任務(wù)...");
// 定義多個(gè)返回結(jié)果的異步任務(wù)
Task<int> task1 = SimulateTaskAsync("任務(wù)1", 2000, 10);
Task<int> task2 = SimulateTaskAsync("任務(wù)2", 1000, 20);
Task<int> task3 = SimulateTaskAsync("任務(wù)3", 3000, 30);
// 將任務(wù)放入一個(gè)數(shù)組
Task<int>[] tasks = { task1, task2, task3 };
// 使用 ReadOnlySpan<Task<TResult>>
ReadOnlySpan<Task<int>> taskSpan = new ReadOnlySpan<Task<int>>(tasks);
// 使用 Task.WhenAll 等待所有任務(wù)完成并獲取結(jié)果
int[] results = await Task.WhenAll(taskSpan);
Console.WriteLine("所有任務(wù)已完成,結(jié)果如下:");
foreach (var result in results)
{
Console.WriteLine($"任務(wù)返回結(jié)果:{result}");
}
}
// 模擬異步任務(wù),返回一個(gè)整數(shù)結(jié)果
static async Task<int> SimulateTaskAsync(string taskName, int delay, int result)
{
Console.WriteLine($"{taskName} 開始執(zhí)行,預(yù)計(jì)耗時(shí) {delay} 毫秒...");
await Task.Delay(delay); // 模擬異步操作
Console.WriteLine($"{taskName} 執(zhí)行完成,結(jié)果:{result}");
return result;
}
}
}運(yùn)行:

WhenAll<TResult>(IEnumerable<Task<TResult>>)
創(chuàng)建一個(gè)任務(wù),該任務(wù)將在可枚舉集合中的所有 Task<TResult> 對象完成時(shí)完成。
public static System.Threading.Tasks.Task<TResult[]> WhenAll<TResult> (System.Collections.Generic.IEnumerable<System.Threading.Tasks.Task<TResult>> tasks);
類型參數(shù)
TResult
已完成任務(wù)的類型。
參數(shù)
tasks IEnumerable<Task<TResult>>
要等待完成的任務(wù)。
返回
Task<TResult[]>
表示所有提供任務(wù)的完成的任務(wù)。
例外
ArgumentNullException
tasks 參數(shù) null。
ArgumentException
tasks 集合包含 null 任務(wù)。
代碼:
namespace WhenAllTest3
{
internal class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("開始執(zhí)行任務(wù)...");
// 定義多個(gè)返回結(jié)果的異步任務(wù)
List<Task<int>> tasks = new List<Task<int>>
{
SimulateTaskAsync("任務(wù)1", 2000, 10),
SimulateTaskAsync("任務(wù)2", 1000, 20),
SimulateTaskAsync("任務(wù)3", 3000, 30)
};
// 使用 Task.WhenAll 等待所有任務(wù)完成并獲取結(jié)果
int[] results = await Task.WhenAll(tasks);
Console.WriteLine("所有任務(wù)已完成,結(jié)果如下:");
foreach (var result in results)
{
Console.WriteLine($"任務(wù)返回結(jié)果:{result}");
}
// 進(jìn)一步處理結(jié)果
Console.WriteLine($"任務(wù)結(jié)果總和:{results.Sum()}");
}
// 模擬異步任務(wù),返回一個(gè)整數(shù)結(jié)果
static async Task<int> SimulateTaskAsync(string taskName, int delay, int result)
{
Console.WriteLine($"{taskName} 開始執(zhí)行,預(yù)計(jì)耗時(shí) {delay} 毫秒...");
await Task.Delay(delay); // 模擬異步操作
Console.WriteLine($"{taskName} 執(zhí)行完成,結(jié)果:{result}");
return result;
}
}
}運(yùn)行:

WhenAll<TResult>(Task<TResult>[])
創(chuàng)建一個(gè)任務(wù),該任務(wù)將在數(shù)組中的所有 Task<TResult> 對象完成時(shí)完成。
public static System.Threading.Tasks.Task<TResult[]> WhenAll<TResult> (params System.Threading.Tasks.Task<TResult>[] tasks);
類型參數(shù)
TResult
已完成任務(wù)的類型。
參數(shù)
tasks Task<TResult>[]
要等待完成的任務(wù)。
返回
Task<TResult[]>
表示所有提供任務(wù)的完成的任務(wù)。
例外
ArgumentNullException
tasks 參數(shù) null。
ArgumentException
tasks 數(shù)組包含 null 任務(wù)。
代碼:
namespace WhenAllTest3
{
internal class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("開始執(zhí)行任務(wù)...");
// 定義多個(gè)返回結(jié)果的異步任務(wù)
Task<int> task1 = SimulateTaskAsync("任務(wù)1", 2000, 10);
Task<int> task2 = SimulateTaskAsync("任務(wù)2", 1000, 20);
Task<int> task3 = SimulateTaskAsync("任務(wù)3", 3000, 30);
// 將任務(wù)放入數(shù)組
Task<int>[] tasks = { task1, task2, task3 };
// 使用 Task.WhenAll 等待所有任務(wù)完成并獲取結(jié)果
int[] results = await Task.WhenAll(tasks);
Console.WriteLine("所有任務(wù)已完成,結(jié)果如下:");
foreach (var result in results)
{
Console.WriteLine($"任務(wù)返回結(jié)果:{result}");
}
// 進(jìn)一步處理結(jié)果
int sum = 0;
foreach (var result in results)
{
sum += result;
}
Console.WriteLine($"任務(wù)結(jié)果總和:{sum}");
}
// 模擬異步任務(wù),返回一個(gè)整數(shù)結(jié)果
static async Task<int> SimulateTaskAsync(string taskName, int delay, int result)
{
Console.WriteLine($"{taskName} 開始執(zhí)行,預(yù)計(jì)耗時(shí) {delay} 毫秒...");
await Task.Delay(delay); // 模擬異步操作
Console.WriteLine($"{taskName} 執(zhí)行完成,結(jié)果:{result}");
return result;
}
}
}結(jié)果:

到此這篇關(guān)于C# Task.WhenAll的用法小結(jié)的文章就介紹到這了,更多相關(guān)C# Task.WhenAll 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#開發(fā)WinForm根據(jù)條件改變DataGridView行顏色
這篇文章介紹了C#開發(fā)WinForm根據(jù)條件改變DataGridView行顏色的方法,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03
詳解三種C#實(shí)現(xiàn)數(shù)組反轉(zhuǎn)方式
本篇文章主要介紹了詳解三種C#實(shí)現(xiàn)數(shù)組反轉(zhuǎn)方式,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-04-04
C#實(shí)現(xiàn)批量更改文件名稱大小寫或擴(kuò)展名
這篇文章主要為大家詳細(xì)介紹了如何利用C#實(shí)現(xiàn)批量更改文件名稱大小寫或擴(kuò)展名的功能,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)C#有一定的幫助,感興趣的小伙伴可以跟隨小編一起了解一下2022-12-12
C/C++ 傳遞動(dòng)態(tài)內(nèi)存的深入理解
本篇文章是對C/C++中的傳遞動(dòng)態(tài)內(nèi)存進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05
C#?使用SpecFlow創(chuàng)建BDD測試用例的示例代碼
這篇文章主要介紹了C#?使用SpecFlow創(chuàng)建BDD測試用例,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06
C#操作SQLite實(shí)現(xiàn)數(shù)據(jù)的增刪改查
SQLite是一個(gè)輕量級、跨平臺(tái)的關(guān)系型數(shù)據(jù)庫,在小型項(xiàng)目中,方便,易用,同時(shí)支持多種開發(fā)語言。本文將用C#語言對SQLite 的一個(gè)封裝,實(shí)現(xiàn)數(shù)據(jù)的增刪改查。需要的可以參考一下2022-01-01

