概述
async/await 是 C# 5.0 引入的异步编程模型,让异步代码看起来和同步代码相似,大幅简化异步编程。
基础概念
async 关键字
标记方法为异步方法,允许使用 await。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public async Task DoSomethingAsync() { await Task.Delay(1000); }
public async Task<string> GetDataAsync() { await Task.Delay(500); return "完成"; }
private async void Button_Click(object sender, EventArgs e) { await LoadDataAsync(); }
|
await 关键字
暂停方法执行直到等待的操作完成,期间不会阻塞线程。
1 2 3 4 5 6 7 8 9 10 11
| public async Task<string> FetchDataAsync() { HttpClient client = new HttpClient(); string result = await client.GetStringAsync("https://api.example.com/data"); return result; }
|
执行流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| public async Task DemoAsync() { Console.WriteLine("1. 开始"); Task<string> task = LongOperationAsync(); Console.WriteLine("2. 操作发起,未阻塞"); string result = await task; Console.WriteLine($"3. 操作完成: {result}"); Console.WriteLine("4. 继续执行"); }
private async Task<string> LongOperationAsync() { Console.WriteLine(" 内部: 开始工作"); await Task.Delay(1000); Console.WriteLine(" 内部: 工作完成"); return "成功"; }
|
返回值类型
| 返回类型 |
适用场景 |
Task |
无返回值的异步操作 |
Task<T> |
有返回值的异步操作 |
ValueTask / ValueTask<T> |
高频调用、结果可能同步返回的场景 |
void |
仅限事件处理程序 |
Task vs ValueTask
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public async Task<int> GetIdAsync() { await Task.Delay(10); return 42; }
public ValueTask<int> GetCachedValueAsync() { if (_cachedValue != null) return new ValueTask<int>(_cachedValue.Value); return new ValueTask<int>(LoadValueAsync()); }
private async Task<int> LoadValueAsync() { await Task.Delay(100); return 42; }
|
异常处理
async/await 异常不会丢失,像同步代码一样捕获。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public async Task HandleExceptionAsync() { try { await FaultyOperationAsync(); } catch (InvalidOperationException ex) { Console.WriteLine($"捕获异常: {ex.Message}"); } }
private async Task FaultyOperationAsync() { await Task.Delay(100); throw new InvalidOperationException("出错了"); }
|
多个异步操作异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public async Task HandleMultipleAsync() { var tasks = new List<Task> { FaultyOperationAsync(), AnotherFaultyAsync() };
try { await Task.WhenAll(tasks); } catch (Exception ex) { Console.WriteLine(ex.Message); foreach (var task in tasks) { if (task.IsFaulted && task.Exception != null) { foreach (var inner in task.Exception.InnerExceptions) { Console.WriteLine(inner.Message); } } } } }
|
控制 await 是否恢复原 SynchronizationContext。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public async Task ConfigureAwaitDemoAsync() { await Task.Delay(1000).ConfigureAwait(false); }
public async Task ButtonClickHandlerAsync() { await Task.Delay(1000); this.TextBox.Text = "完成"; }
|
避免死锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public string SyncWrapped() { return GetDataAsync().Result; }
public async Task<string> CorrectAsync() { return await GetDataAsync(); }
public string Workaround() { return Task.Run(async () => await GetDataAsync().ConfigureAwait(false)) .Result; }
|
组合异步操作
顺序执行
1 2 3 4 5 6 7
| public async Task SequentialAsync() { var result1 = await Operation1Async(); var result2 = await Operation2Async(); var result3 = await Operation3Async(); }
|
并行执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public async Task ParallelAsync() { var task1 = Operation1Async(); var task2 = Operation2Async(); var task3 = Operation3Async(); await Task.WhenAll(task1, task2, task3); }
public async Task<string[]> FetchAllAsync() { var tasks = urls.Select(url => httpClient.GetStringAsync(url)); return await Task.WhenAll(tasks); }
|
竞速执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public async Task<string> RaceAsync() { Task<string> task1 = GetFromCacheAsync(); Task<string> task2 = GetFromDatabaseAsync(); Task<string> completed = await Task.WhenAny(task1, task2); return await completed; }
public async Task<string> WithTimeoutAsync(TimeSpan timeout) { using var cts = new CancellationTokenSource(timeout); try { return await FetchAsync(cts.Token); } catch (OperationCanceledException) { return "超时"; } }
|
IAsyncEnumerable(异步流)
C# 8.0+ 支持异步枚举。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public async IAsyncEnumerable<int> GenerateNumbersAsync() { for (int i = 0; i < 10; i++) { await Task.Delay(100); yield return i; } }
public async Task ConsumeAsync() { await foreach (var number in GenerateNumbersAsync()) { Console.WriteLine($"收到: {number}"); } }
|
完整示例:异步 Web 请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| using System; using System.Collections.Generic; using System.Net.Http; using System.Threading.Tasks;
class AsyncExample { private static readonly HttpClient _client = new HttpClient(); public static async Task Main() { var urls = new[] { "https://api.github.com/users/octocat", "https://api.github.com/users/defunkt", "https://api.github.com/users/kevin" }; _client.DefaultRequestHeaders.UserAgent.ParseAdd("AsyncApp/1.0"); await ProcessUrlsAsync(urls); } private static async Task ProcessUrlsAsync(string[] urls) { var tasks = new List<Task<(string url, int length)>>(); foreach (var url in urls) { tasks.Add(FetchAndMeasureAsync(url)); } var results = await Task.WhenAll(tasks); foreach (var (url, length) in results) { Console.WriteLine($"{url}: {length} 字符"); } } private static async Task<(string, int)> FetchAndMeasureAsync(string url) { try { string content = await _client.GetStringAsync(url); return (url, content.Length); } catch (HttpRequestException ex) { Console.WriteLine($"请求 {url} 失败: {ex.Message}"); return (url, 0); } } }
|
最佳实践
| 原则 |
说明 |
| 一路 async |
尽量保持 async 调用链,不要混用同步等待 |
| 避免 async void |
仅用于事件处理程序 |
| 库代码使用 ConfigureAwait(false) |
避免死锁,提升性能 |
| 使用 CancellationToken |
支持取消操作 |
| 不要阻塞异步代码 |
避免 .Result, .Wait() |
| 适当使用 ValueTask |
高频调用或热路径 |
参考资源