异步编程
理解异步编程的原理与价值
掌握 async/await 语法
能编写异步方法与异步 API
掌握异步最佳实践与常见陷阱
核心讲解
(1)为什么需要异步
同步调用会阻塞当前线程,等待期间线程闲着浪费;异步调用不会阻塞,线程去做其他事,完成了再回来继续,大幅提升吞吐量。
适用:IO 密集型任务(网络请求、文件读写、数据库)
不适用:CPU 密集型任务,并行更合适
(2)核心语法
async:修饰方法,标记方法是异步方法await:等待异步任务,不会阻塞线程,执行到 await 方法返回,等任务完成再往下走返回值:
Task无返回值、Task<T>有返回值命名约定:异步方法名以
Async结尾
(3)常用异步 API
.NET 很多内置 API 都有异步版本:
文件:
File.ReadAllTextAsync()、WriteAllTextAsync()网络:
HttpClient.GetAsync()、PostAsync()数据库:EF Core 异步查询
(4)组合器
Task.WhenAll(t1,t2...):等待所有任务完成Task.WhenAny(t1,t2...):等待任意一个完成Task.Delay():异步等待
操作步骤
编写异步方法,模拟异步 IO
用 WhenAll 等待多个任务
运行观察执行顺序
代码示例
using System.Diagnostics;
Stopwatch sw = Stopwatch.StartNew();
// 异步方法
async Task<string> DownloadDataAsync(string url)
{
Console.WriteLine($"开始下载:{url}");
await Task.Delay(1000); // 模拟网络耗时
Console.WriteLine($"下载完成:{url}");
return $"{url} 的内容";
}
// ===== 1. 串行等待 =====
Console.WriteLine("=== 串行异步 ===");
sw.Restart();
string r1 = await DownloadDataAsync("url1");
string r2 = await DownloadDataAsync("url2");
string r3 = await DownloadDataAsync("url3");
Console.WriteLine($"串行耗时:{sw.ElapsedMilliseconds}ms");
// ===== 2. 并行等待所有 =====
Console.WriteLine("\n=== 并行异步 ===");
sw.Restart();
Task<string> t1 = DownloadDataAsync("url1");
Task<string> t2 = DownloadDataAsync("url2");
Task<string> t3 = DownloadDataAsync("url3");
string[] results = await Task.WhenAll(t1, t2, t3);
Console.WriteLine($"并行耗时:{sw.ElapsedMilliseconds}ms");
// ===== 3. 异步文件读写 =====
string file = "async_test.txt";
await File.WriteAllTextAsync(file, "Hello 异步编程");
string content = await File.ReadAllTextAsync(file);
Console.WriteLine("\n文件内容:" + content);
Console.ReadKey();
常见误区
async void:除了事件处理,永远不要写 async void,异常无法捕获,无法等待
Wait ()/Result () 死锁:UI 线程、ASP.NET 里用 Result/Wait 容易死锁,全程用 await
假异步:方法里没有 await 还标 async,或者用 Task.Run 包装同步方法
异步方法加后缀:约定异步方法名以 Async 结尾,方便识别
课后练习
写一个异步方法,模拟耗时操作,返回随机数
同时启动 5 个异步任务,等待全部完成,计算总耗时
用异步方式读写一个文件
简述异步和多线程的区别?

