跳到主要内容

C# 异步编程

本章将详细介绍 C# 中的异步编程,包括 async/await 语法、Task 并行库、以及异步模式。

异步编程概述

什么是异步编程?

异步编程允许程序在等待耗时操作(如 I/O、网络请求)完成时继续执行其他工作,提高程序的整体吞吐量和响应性。

同步执行:
┌─────────────────────────────┐
│ ████████████████████████████ │ 等待 2 秒
└─────────────────────────────┘
执行 等待 完成

异步执行:
┌─────────┐ ┌─────────┐
│ 执行 │ │ 其他 │ ← 可以同时执行
└────┬────┘ └────┬────┘
│ │
▼ ▼
┌─────────┐ ┌─────────┐
│ 等待完成 │ │ 完成 │
└─────────┘ └─────────┘

async 和 await 基础

基本语法

// 异步方法定义
async Task<int> DownloadAsync(string url)
{
// 模拟下载
await Task.Delay(1000);
return 100;
}

// 调用异步方法
async Task Main()
{
int result = await DownloadAsync("http://example.com");
Console.WriteLine($"下载完成: {result}");
}

完整示例

class AsyncExample
{
// 异步方法
public async Task<string> GetDataAsync()
{
Console.WriteLine("开始获取数据...");

// 模拟耗时操作
await Task.Delay(2000);

Console.WriteLine("数据获取完成");
return "数据内容";
}

// 主程序
public static async Task Main()
{
var example = new AsyncExample();
string data = await example.GetDataAsync();
Console.WriteLine($"收到数据: {data}");
}
}

Task 和 Task<T>

Task 基础

// Task 表示一个异步操作
Task task = Task.Run(() =>
{
Console.WriteLine("在后台执行");
});

await task; // 等待完成

// Task\<T> 返回结果
Task\<int> taskWithResult = Task.Run(() =>
{
return 42;
});

int result = await taskWithResult;
Console.WriteLine(result); // 42

创建 Task 的方式

// 方式1:Task.Run
Task t1 = Task.Run(() => DoWork());

// 方式2:Task.Factory.StartNew
Task t2 = Task.Factory.StartNew(() => DoWork());

// 方式3:new Task
Task t3 = new Task(() => DoWork());
t3.Start();

// 方式4:Task.FromResult(返回已完成的任务)
Task<string> t4 = Task.FromResult("已完成");

返回值处理

// 异步方法返回 Task 或 Task\<T>
async Task DoSomethingAsync()
{
await Task.Delay(1000);
}

async Task<int> CalculateAsync()
{
await Task.Delay(1000);
return 42;
}

// 错误示范:返回 Task 但用 void(避免)
// async void Wrong() { } // 不要这样写

// 正确:返回 Task
async Task Correct() { }

await 表达式

await 的行为

async Task Method1()
{
// await 暂停当前方法,等待 Task 完成
await Task.Delay(1000);
Console.WriteLine("Task 完成");
}

// 并行执行多个异步操作
async Task ParallelAsync()
{
var t1 = Task.Delay(1000);
var t2 = Task.Delay(2000);
var t3 = Task.Delay(500);

await Task.WhenAll(t1, t2, t3); // 等待所有完成
Console.WriteLine("全部完成");
}

ConfigureAwait

// 默认为 true:在原始上下文中恢复
async Task DefaultBehavior()
{
await Task.Delay(1000);
// 可能在原始同步上下文(UI 线程)中执行后续代码
}

// false:不需要恢复上下文,可能在任意线程池线程执行
async Task NoConfigureAwait()
{
await Task.Delay(1000).ConfigureAwait(false);
// 不会尝试恢复到原始上下文
}

异步链式调用

async Task<string> Step1() 
{
await Task.Delay(100);
return "Step1";
}

async Task<string> Step2(string input)
{
await Task.Delay(100);
return input + " -> Step2";
}

async Task ProcessAsync()
{
string result = await Step1().ContinueWith(async t =>
await Step2(t.Result)).Unwrap();
Console.WriteLine(result); // Step1 -> Step2
}

异常处理

try-catch 捕获异常

async Task ErrorHandlingAsync()
{
try
{
await RiskyOperationAsync();
}
catch (Exception ex)
{
Console.WriteLine($"异常: {ex.Message}");
}
}

多个异步操作的异常

async Task MultipleExceptionsAsync()
{
try
{
await Task.WhenAll(
Task1Async(),
Task2Async(),
Task3Async()
);
}
catch (Exception ex)
{
// AggregateException 包含所有异常
var aggregate = (AggregateException)ex;
foreach (var e in aggregate.InnerExceptions)
{
Console.WriteLine(e.Message);
}
}
}

使用 WhenAny 处理超时

async Task WithTimeoutAsync()
{
var task = LongRunningOperationAsync();
var timeout = Task.Delay(3000);

var completed = await Task.WhenAny(task, timeout);

if (completed == task)
{
var result = await task;
Console.WriteLine($"结果: {result}");
}
else
{
Console.WriteLine("超时");
}
}

取消操作

CancellationToken

async Task CancellationExampleAsync()
{
using var cts = new CancellationTokenSource();

// 5秒后取消
cts.CancelAfter(5000);

try
{
await LongRunningOperationAsync(cts.Token);
}
catch (OperationCanceledException)
{
Console.WriteLine("操作已取消");
}
}

async Task LongRunningOperationAsync(CancellationToken token)
{
for (int i = 0; i < 10; i++)
{
token.ThrowIfCancellationRequested(); // 检查取消
await Task.Delay(500, token);
Console.WriteLine($"完成 {i + 1}/10");
}
}

并行执行

Task.WhenAll

async Task WhenAllExample()
{
var tasks = new List<Task<int>>();

for (int i = 0; i < 5; i++)
{
int id = i;
tasks.Add(Task.Run(() => id * id));
}

int[] results = await Task.WhenAll(tasks);
Console.WriteLine(string.Join(", ", results)); // 0, 1, 4, 9, 16
}

Task.WhenAny

async Task WhenAnyExample()
{
var tasks = new Task<string>[]
{
Task.Run(() => { Thread.Sleep(1000); return "A完成"; }),
Task.Run(() => { Thread.Sleep(500); return "B完成"; }),
Task.Run(() => { Thread.Sleep(2000); return "C完成"; })
};

var completedTask = await Task.WhenAny(tasks);
string result = await completedTask;
Console.WriteLine($"最先完成: {result}"); // B完成
}

异步模式

异步编程模式

// EAP(基于事件的异步模式)- 旧式
class AsyncExampleEAP
{
public event Action<int>? ProgressChanged;

public void DoWorkAsync()
{
// 后台工作
for (int i = 0; i < 100; i++)
{
Thread.Sleep(50);
ProgressChanged?.Invoke(i);
}
}
}

// TAP(基于任务的异步模式)- 推荐
class AsyncExampleTAP
{
public async Task<int> DoWorkAsync()
{
int result = 0;
for (int i = 0; i < 100; i++)
{
await Task.Delay(50);
result += i;
}
return result;
}
}

ValueTask

// ValueTask:用于快速同步完成的场景,避免分配 Task 对象
async ValueTask<bool> QuickCheckAsync()
{
if (cache.TryGetValue("key", out var value))
{
return true; // 同步完成
}
// 异步完成
await Task.Delay(100);
return false;
}

IAsyncEnumerable(C# 8+)

// 异步迭代器
async IAsyncEnumerable<int> GenerateNumbersAsync()
{
for (int i = 0; i < 10; i++)
{
await Task.Delay(100);
yield return i;
}
}

// 使用
await foreach (var number in GenerateNumbersAsync())
{
Console.WriteLine(number);
}

异步 LINQ

// 使用 System.Linq.Async
var result = await numbers
.ToAsyncEnumerable()
.Where(x => x % 2 == 0)
.Select(x => x * 2)
.ToListAsync();

小结

  1. async/await:简化异步编程的核心语法
  2. Task:表示异步操作
  3. Task<T>:返回结果的异步操作
  4. ConfigureAwait:控制上下文恢复
  5. WhenAll/WhenAny:并行执行多个异步操作
  6. CancellationToken:支持取消操作

练习

  1. 实现一个带超时控制的 HTTP 请求方法
  2. 实现批量下载文件的功能(并行)
  3. 实现一个可取消的进度报告功能
  4. 使用 IAsyncEnumerable 实现异步数据流