异步编程基础
async/await 语法详解
任务操作(Task)
取消支持(CancellationToken)
异常处理
最佳实践与陷阱
实战场景
性能优化
速查表
异步编程:让程序在等待耗时操作(如I/O、网络请求)时,不阻塞当前线程,从而提高应用程序的响应性和吞吐量。
// ❌ 同步方式:线程被阻塞
void SyncMethod()
{
Console.WriteLine("开始");
Thread.Sleep(3000); // 主线程被阻塞3秒
Console.WriteLine("结束");
}
// 输出:开始 →(等待3秒)→ 结束
// ✅ 异步方式:线程不被阻塞
async Task AsyncMethod()
{
Console.WriteLine("开始");
await Task.Delay(3000); // 线程不被阻塞,可以处理其他任务
Console.WriteLine("结束");
}
// 输出:开始 →(立即返回)→ 3秒后输出结束场景 | 示例 |
|---|---|
网络请求 | HTTP API调用、下载文件 |
数据库操作 | 查询、插入、更新 |
文件I/O | 读写大文件 |
UI应用 | 避免界面卡顿 |
Web服务器 | 处理并发请求 |
// 异步方法的完整结构
[访问修饰符] async [Task|Task<T>|void] 方法名(参数列表)
{
// 方法体
await 异步操作;
// 继续执行
}
// 示例
public async Task<string> FetchDataAsync(string url)
{
using var client = new HttpClient();
string result = await client.GetStringAsync(url);
return result;
}返回类型 | 说明 | 使用场景 |
|---|---|---|
| 无返回值 | 异步操作不返回结果 |
| 有返回值(T是返回类型) | 异步操作返回结果 |
| 无返回值(特殊) | 仅限事件处理器 |
| 有返回值(性能优化) | 高频调用、可能同步完成 |
// 1. Task:无返回值
async Task DoWorkAsync()
{
await Task.Delay(1000);
Console.WriteLine("完成");
}
// 2. Task<T>:有返回值
async Task<int> CalculateAsync()
{
await Task.Delay(1000);
return 42;
}
// 3. void:仅限事件处理器
async void Button_Click(object sender, EventArgs e)
{
await LoadDataAsync();
}
// 4. ValueTask<T>:性能优化
async ValueTask<int> GetValueAsync()
{
if (cached)
return cachedValue; // 同步返回,无堆内存分配
return await FetchFromDatabaseAsync();
}// 异步方法的执行流程
async Task ExampleAsync()
{
Console.WriteLine("1. 方法开始(同步执行)");
// 遇到await,线程返回调用者
await Task.Delay(1000);
// 异步操作完成后,继续执行(可能在原线程或新线程)
Console.WriteLine("2. 异步操作完成后继续");
// 多个await
await Task.Delay(500);
Console.WriteLine("3. 第二个异步操作完成");
}// 1. 从异步方法获取Task
Task task = DoWorkAsync();
Task<int> taskWithResult = GetValueAsync();
// 2. 手动创建任务
Task manualTask = Task.Run(() =>
{
// 耗时操作
Thread.Sleep(1000);
Console.WriteLine("任务完成");
});
// 3. 从结果创建Task
Task<int> completedTask = Task.FromResult(42);
// 4. 创建已完成的任务
Task completed = Task.CompletedTask;
// 5. 创建异常任务
Task faultyTask = Task.FromException(new Exception("错误"));
// 6. 取消的任务
var cts = new CancellationTokenSource();
Task canceledTask = Task.FromCanceled(cts.Token);// 等待单个任务
async Task SingleTaskExample()
{
Task<int> task = GetValueAsync();
int result = await task;
Console.WriteLine(result);
}
// 等待所有任务完成(并行执行)
async Task WhenAllExample()
{
Task task1 = DoWorkAsync(1000);
Task task2 = DoWorkAsync(2000);
Task task3 = DoWorkAsync(3000);
await Task.WhenAll(task1, task2, task3);
Console.WriteLine("所有任务完成!");
}
// 等待任一任务完成
async Task WhenAnyExample()
{
Task<string> downloadTask = DownloadAsync();
Task timeoutTask = Task.Delay(5000);
Task completed = await Task.WhenAny(downloadTask, timeoutTask);
if (completed == downloadTask)
{
string result = await downloadTask;
Console.WriteLine($"下载成功:{result}");
}
else
{
Console.WriteLine("下载超时");
}
}
// 等待所有任务完成并获取结果
async Task WhenAllWithResultExample()
{
Task<int>[] tasks = new[]
{
CalculateAsync(1, 100),
CalculateAsync(2, 100),
CalculateAsync(3, 100)
};
int[] results = await Task.WhenAll(tasks);
Console.WriteLine($"结果:{string.Join(", ", results)}");
Console.WriteLine($"总和:{results.Sum()}");
}void CheckTaskStatus(Task task)
{
Console.WriteLine($"任务状态:{task.Status}");
Console.WriteLine($"是否完成:{task.IsCompleted}");
Console.WriteLine($"是否出错:{task.IsFaulted}");
Console.WriteLine($"是否取消:{task.IsCanceled}");
Console.WriteLine($"是否完成(含错误和取消):{task.IsCompleted}");
}
// 任务状态枚举
// - Created:已创建未启动
// - WaitingForActivation:等待激活
// - Running:正在运行
// - WaitingForChildrenToComplete:等待子任务完成
// - RanToCompletion:成功完成
// - Faulted:出错
// - Canceled:取消async Task DownloadAsync(CancellationToken token)
{
// 检查取消
token.ThrowIfCancellationRequested();
using var client = new HttpClient();
// 传递token给支持取消的API
var response = await client.GetAsync("https://example.com", token);
var content = await response.Content.ReadAsStringAsync(token);
return content;
}// 1. 手动取消
void ManualCancel()
{
var cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
// 注册取消回调
token.Register(() => Console.WriteLine("操作已取消"));
// 稍后取消
cts.Cancel(); // 触发取消
}
// 2. 超时自动取消
void TimeoutCancel()
{
var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(5));
CancellationToken token = cts.Token;
}
// 3. 组合取消
void CombineCancel()
{
var cts1 = new CancellationTokenSource();
var cts2 = new CancellationTokenSource();
// 任一取消都触发取消
var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(
cts1.Token, cts2.Token
);
CancellationToken token = linkedCts.Token;
}async Task ProcessItemsAsync(List<string> items, CancellationToken token)
{
for (int i = 0; i < items.Count; i++)
{
// 方式1:抛出异常
token.ThrowIfCancellationRequested();
// 方式2:检查并手动处理
if (token.IsCancellationRequested)
{
Console.WriteLine("操作已取消");
return;
}
await ProcessItemAsync(items[i], token);
}
}
// 支持取消的异步操作
async Task DownloadWithProgressAsync(CancellationToken token)
{
using var client = new HttpClient();
// 获取响应
using var response = await client.GetAsync(
"https://example.com/largefile.zip",
HttpCompletionOption.ResponseHeadersRead,
token
);
var totalBytes = response.Content.Headers.ContentLength ?? -1;
using var stream = await response.Content.ReadAsStreamAsync(token);
using var fileStream = File.Create("file.zip");
var buffer = new byte[8192];
int bytesRead;
long totalRead = 0;
while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, token)) > 0)
{
await fileStream.WriteAsync(buffer, 0, bytesRead, token);
totalRead += bytesRead;
if (totalBytes > 0)
{
int progress = (int)((double)totalRead / totalBytes * 100);
Console.WriteLine($"进度:{progress}%");
}
token.ThrowIfCancellationRequested();
}
}async Task HandleExceptionsAsync()
{
try
{
await DownloadDataAsync();
Console.WriteLine("下载成功");
}
catch (HttpRequestException ex)
{
Console.WriteLine($"网络错误:{ex.Message}");
}
catch (OperationCanceledException ex)
{
Console.WriteLine($"操作已取消:{ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"未知错误:{ex.Message}");
}
}async Task MultipleTasksExceptionExample()
{
Task task1 = ThrowExceptionAsync("任务1");
Task task2 = ThrowExceptionAsync("任务2");
try
{
await Task.WhenAll(task1, task2);
}
catch (Exception ex)
{
// 只捕获第一个异常
Console.WriteLine($"捕获异常:{ex.Message}");
// 获取所有异常
if (task1.IsFaulted)
{
foreach (var inner in task1.Exception.InnerExceptions)
Console.WriteLine($"任务1:{inner.Message}");
}
if (task2.IsFaulted)
{
foreach (var inner in task2.Exception.InnerExceptions)
Console.WriteLine($"任务2:{inner.Message}");
}
}
}
// 完整的异常信息获取
async Task GetAllExceptionsExample()
{
try
{
await ThrowExceptionAsync("任务1");
await ThrowExceptionAsync("任务2");
}
catch (Exception ex)
{
// 获取所有异常(包括内部异常)
var allExceptions = GetInnerExceptions(ex);
foreach (var e in allExceptions)
{
Console.WriteLine(e.Message);
}
}
}
IEnumerable<Exception> GetInnerExceptions(Exception ex)
{
if (ex is AggregateException agg)
{
foreach (var inner in agg.InnerExceptions)
{
foreach (var e in GetInnerExceptions(inner))
yield return e;
}
}
else if (ex.InnerException != null)
{
yield return ex;
foreach (var e in GetInnerExceptions(ex.InnerException))
yield return e;
}
else
{
yield return ex;
}
}// ❌ 危险:async void 中的异常会丢失
async void BadButtonClick(object sender, EventArgs e)
{
throw new Exception("这个异常会崩溃应用程序!");
}
// ✅ 安全:在事件处理器中捕获异常
async void SafeButtonClick(object sender, EventArgs e)
{
try
{
await LoadDataAsync();
}
catch (Exception ex)
{
Console.WriteLine($"加载失败:{ex.Message}");
// 显示给用户
}
}// ❌ 危险:可能导致死锁
string BadSyncCall()
{
// 同步等待异步任务
var task = GetDataAsync();
string result = task.Result; // 可能死锁!
return result;
}
// ❌ 危险:也可能死锁
string BadSyncCall2()
{
return GetDataAsync().GetAwaiter().GetResult();
}
// ✅ 正确:一直异步到底
async Task<string> GoodAsyncCall()
{
return await GetDataAsync();
}
// ✅ 替代:使用 ConfigureAwait(false)(适用于类库)
string SafeSyncCall()
{
return GetDataAsync().ConfigureAwait(false).GetAwaiter().GetResult();
}// UI应用(WinForms/WPF)
async void ButtonClick(object sender, EventArgs e)
{
// 默认捕获UI上下文
var data = await GetDataAsync();
// 回到UI线程更新界面
label.Text = data;
}
// 类库代码(推荐)
async Task<string> FetchDataAsync()
{
// 不捕获上下文,提升性能
using var client = new HttpClient();
string result = await client.GetStringAsync("https://api.example.com")
.ConfigureAwait(false);
return result;
}
// 多个await的情况
async Task<string> ComplexExampleAsync()
{
// 第一个await不捕获上下文
var data = await GetDataAsync().ConfigureAwait(false);
// 第二个await也不捕获上下文(明确指定)
var processed = await ProcessDataAsync(data).ConfigureAwait(false);
return processed;
}// ❌ 不推荐:除了事件处理器
async void BadMethod()
{
// 异常无法被调用者捕获
throw new Exception("丢失的异常");
}
// ✅ 推荐:使用Task
async Task GoodMethod()
{
// 异常可被捕获
throw new Exception("可捕获的异常");
}// ❌ 不必要:方法只有一行async/await
async Task<int> OverAsync()
{
return await Task.FromResult(42);
}
// ✅ 更简单:同步返回
int Simple()
{
return 42;
}
// ✅ 如果有条件判断,可以使用 ValueTask
async ValueTask<int> BetterAsync()
{
if (cached)
return 42; // 同步返回,不分配内存
return await FetchFromDbAsync();
}// ❌ 避免:用Task.Run包装纯异步方法
async Task BadAsync()
{
// Task.Run会创建线程,但HttpClient已经是异步
await Task.Run(async () =>
{
using var client = new HttpClient();
await client.GetStringAsync("https://example.com");
});
}
// ✅ 直接调用异步方法
async Task GoodAsync()
{
using var client = new HttpClient();
await client.GetStringAsync("https://example.com");
}
// ✅ Task.Run适用于CPU密集型操作
Task<int> ProcessDataAsync()
{
return Task.Run(() =>
{
// CPU密集型计算
int result = 0;
for (int i = 0; i < 1000000; i++)
result += i;
return result;
});
}// ✅ 异步方法命名以Async结尾(约定)
async Task<string> FetchDataAsync() { }
async Task<int> CalculateAsync() { }
// ❌ 不推荐:不以Async结尾
async Task<string> FetchData() { }
// ✅ 事件处理器可以不加Async后缀
async void Button_Click(object sender, EventArgs e) { }public class ApiService
{
private readonly HttpClient _httpClient = new();
// GET请求
public async Task<T> GetAsync<T>(string url)
{
try
{
var response = await _httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
string json = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<T>(json);
}
catch (HttpRequestException ex)
{
Console.WriteLine($"HTTP请求失败:{ex.Message}");
throw;
}
}
// POST请求
public async Task<T> PostAsync<T>(string url, object data)
{
string json = JsonSerializer.Serialize(data);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync(url, content);
response.EnsureSuccessStatusCode();
string resultJson = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<T>(resultJson);
}
// 文件下载
public async Task DownloadFileAsync(string url, string savePath, IProgress<float> progress)
{
var response = await _httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();
var totalBytes = response.Content.Headers.ContentLength ?? -1;
using var contentStream = await response.Content.ReadAsStreamAsync();
using var fileStream = File.Create(savePath);
byte[] buffer = new byte[8192];
long totalRead = 0;
int bytesRead;
while ((bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
await fileStream.WriteAsync(buffer, 0, bytesRead);
totalRead += bytesRead;
if (totalBytes > 0)
{
progress?.Report((float)totalRead / totalBytes * 100);
}
}
}
}public class UserService
{
private readonly string _connectionString;
public UserService(string connectionString)
{
_connectionString = connectionString;
}
// 异步查询
public async Task<List<User>> GetUsersAsync()
{
var users = new List<User>();
using var connection = new SqlConnection(_connectionString);
await connection.OpenAsync();
using var command = new SqlCommand("SELECT Id, Name, Email FROM Users", connection);
using var reader = await command.ExecuteReaderAsync();
while (await reader.ReadAsync())
{
users.Add(new User
{
Id = reader.GetInt32(0),
Name = reader.GetString(1),
Email = reader.GetString(2)
});
}
return users;
}
// 异步插入
public async Task<int> InsertUserAsync(string name, string email)
{
using var connection = new SqlConnection(_connectionString);
await connection.OpenAsync();
const string sql = "INSERT INTO Users (Name, Email) VALUES (@Name, @Email)";
using var command = new SqlCommand(sql, connection);
command.Parameters.AddWithValue("@Name", name);
command.Parameters.AddWithValue("@Email", email);
return await command.ExecuteNonQueryAsync();
}
}public class FileService
{
// 读取大文件(逐行)
public async Task ProcessFileAsync(string path, Func<string, Task> lineProcessor)
{
using var reader = new StreamReader(path);
string line;
int lineNumber = 0;
while ((line = await reader.ReadLineAsync()) != null)
{
lineNumber++;
await lineProcessor(line);
}
}
// 异步写入
public async Task WriteFileAsync(string path, IEnumerable<string> lines)
{
using var writer = new StreamWriter(path);
foreach (var line in lines)
{
await writer.WriteLineAsync(line);
}
}
// 异步复制(带进度)
public async Task CopyFileAsync(string source, string dest, IProgress<int> progress)
{
using var sourceStream = File.OpenRead(source);
using var destStream = File.Create(dest);
var totalBytes = sourceStream.Length;
var buffer = new byte[8192];
long totalRead = 0;
int bytesRead;
while ((bytesRead = await sourceStream.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
await destStream.WriteAsync(buffer, 0, bytesRead);
totalRead += bytesRead;
int percentage = (int)((double)totalRead / totalBytes * 100);
progress?.Report(percentage);
}
}
}// 适用于高频调用、可能同步完成的情况
public class CacheService
{
private readonly Dictionary<string, string> _cache = new();
private readonly HttpClient _httpClient = new();
// 使用ValueTask优化缓存场景
public async ValueTask<string> GetValueAsync(string key)
{
// 缓存命中:同步返回(无堆内存分配)
if (_cache.TryGetValue(key, out string cached))
return cached;
// 缓存未命中:异步获取
string result = await FetchFromNetworkAsync(key);
_cache[key] = result;
return result;
}
private async Task<string> FetchFromNetworkAsync(string key)
{
var response = await _httpClient.GetStringAsync($"https://api.example.com/{key}");
return response;
}
}// ❌ 不必要:包装同步操作
async Task<int> BadAsync(int a, int b)
{
return await Task.Run(() => a + b); // 多余!
}
// ✅ 同步即可
int GoodSync(int a, int b) => a + b;
// ✅ 需要等待多个结果时使用WhenAll
async Task<int> ProcessMultipleAsync()
{
// 错误:依次执行
// var result1 = await GetData1Async();
// var result2 = await GetData2Async();
// var result3 = await GetData3Async();
// 正确:并行执行
var task1 = GetData1Async();
var task2 = GetData2Async();
var task3 = GetData3Async();
await Task.WhenAll(task1, task2, task3);
return task1.Result + task2.Result + task3.Result;
}方法 | 说明 |
|---|---|
| 异步等待(不阻塞线程) |
| 在ThreadPool上执行代码 |
| 创建已完成的任务 |
| 创建已完成的空任务 |
| 等待所有任务完成 |
| 等待任一任务完成 |
| 创建取消令牌 |
| 避免捕获上下文 |
// 无返回值
public async Task MethodNameAsync(parameters)
{
try
{
await SomeAsyncOperation();
}
catch (Exception ex)
{
// 处理异常
throw;
}
}
// 有返回值
public async Task<T> MethodNameAsync(parameters)
{
try
{
T result = await SomeAsyncOperation();
return result;
}
catch (Exception ex)
{
// 处理异常
throw;
}
}
// 带取消支持
public async Task<T> MethodNameAsync(parameters, CancellationToken token)
{
token.ThrowIfCancellationRequested();
T result = await SomeAsyncOperation(token);
return result;
}
// 带进度报告
public async Task MethodNameAsync(parameters, IProgress<float> progress)
{
progress?.Report(0);
await SomeAsyncOperation();
progress?.Report(100);
}陷阱 | 说明 | 解决方案 |
|---|---|---|
同步等待异步 |
| 使用 |
async void | 异常丢失 | 仅用于事件处理器 |
死锁 |
| 使用 |
过度异步 | 不必要的async包装 | 考虑同步实现 |
忽略取消 | 不支持取消 | 传递 |
异常丢失 | 多个任务异常 | 检查 |