.NET异步、并发与内存管理,如何系统性认知其进阶之路?

摘要:异步编程模式的演进与 TAP 最佳实践 .NET 的异步编程经历了三个时代。理解这段历史不是为了考古,而是因为你在维护老代码时必然会遭遇它们,理解它们才能优雅地迁移。 模式 时代 标志 状态 APM(异步编程模型) .NET 1.x Beg
异步编程模式的演进与 TAP 最佳实践 .NET 的异步编程经历了三个时代。理解这段历史不是为了考古,而是因为你在维护老代码时必然会遭遇它们,理解它们才能优雅地迁移。 模式 时代 标志 状态 APM(异步编程模型) .NET 1.x BeginXxx / EndXxx 已淘汰 EAP(基于事件的异步) .NET 2.0 XxxAsync + XxxCompleted 事件 遗留代码 TAP(基于任务的异步) .NET 4.0+ Task / async / await 推荐使用 TAP 方法的命名与签名规范 很多人写异步方法时忽视规范,导致 API 设计混乱。TAP 有一套严格的约定: // ✅ 标准命名:方法名 + Async 后缀 public Task<int> ReadAsync(byte[] buffer, int offset, int count); // ✅ 已有同名 EAP 方法时,用 TaskAsync 后缀 public Task<string> GetTaskAsync(string url); // ✅ 返回 void 的同步对应版本 → 返回 Task public Task SaveAsync(string path); // ✅ 返回 T 的同步对应版本 → 返回 Task<T> public Task<UserDto> GetUserAsync(int userId); // ❌ 避免:out/ref 参数在 TAP 中禁止使用 // 应将多返回值包装为 tuple 或自定义类型 public Task<(bool Success, string Error)> TryParseAsync(string input); Task 的生命周期:一个经常被忽视的细节 Task 有 冷任务(Cold Task) 和 热任务(Hot Task) 之分。new Task(...) 创建的是冷任务,需要手动调用 Start()。但 TAP 方法返回的 Task 必须是已激活的热任务——调用者不应该也不需要调用 Start()。 ⚠️ 常见错误 如果你在 TAP 方法内部通过 new Task() 构造任务后忘记调用 Start() 就返回它,调用者会陷入永久等待。始终确保返回的 Task 已处于运行状态。 异常处理的正确姿势 异步方法中的异常处理有一个重要原则:参数验证异常应该在 async 方法外层同步抛出,这样调用者能立即捕获,而不必 await 后才能发现错误。 // ✅ 推荐:参数验证在外层同步完成 public Task<int> ProcessAsync(string input) { if (input == null) throw new ArgumentNullException(nameof(input)); // 同步抛出 return ProcessCoreAsync(input); // 委托给真正的 async 方法 } private async Task<int> ProcessCoreAsync(string input) { // 真正的异步工作 var result = await DoWorkAsync(input); return result; } 取消令牌与进度报告:让异步操作可控 写了两三年 .NET,你可能已经在用 CancellationToken,但真正理解它的状态机和设计模式的人并不多。 CancellationToken 的三种终态 Task 状态机: Created ──Start()──▶ Running │ ┌─────────────┼─────────────┐ ▼ ▼ ▼ Canceled Faulted RanToCompletion (取消请求) (未处理异常) (正常完成) │ │ │ └─────────────┴─────────────┘ IsCompleted = true 取消时 Task 进入 Canceled 状态,IsCompleted 返回 true,但 await 它会抛出 OperationCanceledException。
阅读全文