1. TaskCompletionSource 核心概念解析
TaskCompletionSource(TCS)是.NET异步编程模型中的关键组件,它允许开发者手动控制Task的生命周期。与常规async/await模式不同,TCS提供了对任务完成时机、结果和状态的完全掌控权。
1.1 基本工作原理
TCS本质上是一个任务控制器,包含以下核心要素:
- 内部维护一个Task对象(通过Task属性暴露)
- 提供SetResult、SetException、SetCanceled等方法
- 保证状态转换的原子性和线程安全性
典型使用模式如下:
csharp复制var tcs = new TaskCompletionSource<string>();
// 异步操作完成后...
tcs.SetResult("Operation completed");
// 或者
tcs.SetException(new InvalidOperationException());
1.2 与常规Task的对比
常规async方法生成的Task:
- 由编译器自动生成状态机管理
- 执行流程由CLR控制
- 异常自动捕获并封装
TCS创建的Task:
- 完全由开发者手动控制
- 适用于非标准异步场景(如事件回调、线程池工作项等)
- 需要显式处理异常传播
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心API深度剖析
2.1 构造与配置
TCS提供多个构造函数重载:
csharp复制// 默认创建(TaskCreationOptions.None)
var tcs1 = new TaskCompletionSource<string>();
// 指定Task创建选项
var tcs2 = new TaskCompletionSource<string>(
TaskCreationOptions.RunContinuationsAsynchronously);
// 指定状态对象(可用于传递上下文)
var state = new { Id = 123 };
var tcs3 = new TaskCompletionSource<string>(state);
重要提示:RunContinuationsAsynchronously选项能确保延续任务在独立线程执行,避免回调阻塞当前线程。
2.2 状态转换方法
2.2.1 成功完成(SetResult)
csharp复制tcs.SetResult("Success data");
// 等效于:
tcs.TrySetResult("Success data"); // 线程安全版本
2.2.2 异常完成(SetException)
csharp复制tcs.SetException(new InvalidOperationException());
// 支持多个异常:
tcs.SetException(new[] {
new Exception("Error 1"),
new Exception("Error 2")
});
2.2.3 取消操作(SetCanceled)
csharp复制tcs.SetCanceled();
// 带取消令牌的版本:
var cts = new CancellationTokenSource();
cts.Cancel();
tcs.SetCanceled(cts.Token);
3. 高级应用场景
3.1 事件桥接模式
将传统事件模型转换为Task模式:
csharp复制public Task<string> DownloadDataAsync(Uri url)
{
var tcs = new TaskCompletionSource<string>();
var client = new WebClient();
client.DownloadStringCompleted += (s, e) => {
if (e.Error != null)
tcs.SetException(e.Error);
else if (e.Cancelled)
tcs.SetCanceled();
else
tcs.SetResult(e.Result);
};
client.DownloadStringAsync(url);
return tcs.Task;
}
3.2 超时控制实现
结合CancellationTokenSource实现超时:
csharp复制public static Task<T> WithTimeout<T>(this Task<T> task, TimeSpan timeout)
{
var tcs = new TaskCompletionSource<T>();
var cts = new CancellationTokenSource();
cts.Token.Register(() =>
tcs.TrySetCanceled(cts.Token));
cts.CancelAfter(timeout);
task.ContinueWith(t => {
if (t.IsFaulted)
tcs.TrySetException(t.Exception.InnerExceptions);
else if (t.IsCanceled)
tcs.TrySetCanceled();
else
tcs.TrySetR
