异步编程 async/await
翻译整理自 Async Programming 与 Task。
1. async / await 基本用法
public async Task<string> FetchAsync(string url)
{
using var http = new HttpClient();
var text = await http.GetStringAsync(url);
return text;
}
var t = await FetchAsync("https://example.com");
Console.WriteLine(t);async标记方法为异步await等待 Task 完成并自动切回原上下文- 返回类型
Task/Task<T>/ValueTask<T>
2. Task 创建
var t1 = Task.Run(() => Work(1));
var t2 = Task.Factory.StartNew(() => Work(2));
await Task.WhenAll(t1, t2);
await Task.Delay(1000);
var first = await Task.WhenAny(t1, t2);3. 并行与顺序
// 串行
await A();
await B();
// 并行
await Task.WhenAll(A(), B());
// 第一个完成
var r = await Task.WhenAny(servers.Select(s => s.PingAsync()));4. Task 返回值
async Task<int> ComputeAsync()
{
await Task.Delay(100);
return 42;
}
int n = await ComputeAsync();5. 异步取消 CancellationToken
public async Task RunAsync(CancellationToken token)
{
for (int i = 0; i < 1000; i++)
{
token.ThrowIfCancellationRequested();
await Task.Delay(10, token);
}
}
using var cts = new CancellationTokenSource();
cts.CancelAfter(500);
try { await RunAsync(cts.Token); }
catch (OperationCanceledException) { }6. 异常处理
try
{
await Task.WhenAll(t1, t2);
}
catch (Exception e) when (e is not TaskCanceledException)
{
Console.WriteLine(e.Message);
}
WhenAll把所有异常包装为AggregateException,但 await 时只抛第一个。
7. ValueTask
值类型 Task,避免堆分配:
public async ValueTask<int> GetOrCompute()
{
if (cached is not null) return cached.Value; // 同步路径无分配
await Task.Delay(10);
return 0;
}8. ConfigureAwait
await task.ConfigureAwait(false); // 不切回原上下文,库代码推荐
await task.ConfigureAwait(true); // 切回捕获的上下文(默认)库(NuGet 包)应使用
ConfigureAwait(false);UI 应用避免 deadlock。
9. async streams(C# 8+)
public async IAsyncEnumerable<int> StreamAsync(CancellationToken ct = default)
{
for (int i = 0; i < 10; i++)
{
await Task.Delay(100, ct);
yield return i;
}
}
await foreach (var x in StreamAsync())
Console.WriteLine(x);10. 同步上下文与 SynchronizationContext
- UI 应用(WPF/WinForms)有同步上下文,await 后切回 UI 线程
- ASP.NET Core 无同步上下文,所有 await 后任意线程
11. Task.Run vs 调用异步 API
// ❌ 反模式:把 IO 异步包装在线程池
var r = await Task.Run(() => httpClient.GetStringAsync(url));
// ✅ 直接调用
var r = await httpClient.GetStringAsync(url);CPU 密集才用 Task.Run。
12. Parallel 并行循环
Parallel.For(0, 1000, i => Work(i));
Parallel.ForEach(items, item => Work(item));
var sum = ParallelEnumerable.Range(0, 1000).Sum();13. Channel 与生产消费
using System.Threading.Channels;
var ch = Channel.CreateUnbounded<int>();
// 生产
_ = Task.Run(async () =>
{
for (int i = 0; i < 10; i++)
await ch.Writer.WriteAsync(i);
ch.Writer.Complete();
});
// 消费
await foreach (var x in ch.Reader.ReadAllAsync())
Console.WriteLine(x);小结
async/await 是 C# 异步编程的核心,简洁、易读、易维护,配合 Task、CancellationToken、Channel 形成现代高并发模型。