C# TaskCompletionSource.SetResult 用法详解
一、基本概念
TaskCompletionSource<T> 允许手动控制一个 Task 的完成状态,SetResult 用于将任务标记为成功完成并设置结果值。
二、基本用法
1. 创建并设置结果
var tcs = new TaskCompletionSource<int>();// 在某个地方设置结果
tcs.SetResult(42);// 获取结果
int result = await tcs.Task;
Console.WriteLine(result); // 输出: 422. 在异步操作中使用
var tcs = new TaskCompletionSource<string>();// 模拟异步操作
Task.Run(() =>
{Thread.Sleep(1000);tcs.SetResult("操作完成");
});string result = await tcs.Task;
Console.WriteLine(result); // 输出: 操作完成三、实际应用场景
1. 包装传统异步模式
// 将 Event-based 异步模式转换为 Task
public Task<string> DownloadStringAsync(string url)
{var tcs = new TaskCompletionSource<string>();var webClient = new WebClient();webClient.DownloadStringCompleted += (s, e) =>{if (e.Error != null)tcs.SetException(e.Error);else if (e.Cancelled)tcs.SetCanceled();elsetcs.SetResult(e.Result);};webClient.DownloadStringAsync(new Uri(url));return tcs.Task;
}2. 超时控制
public static Task<T> WithTimeout<T>(Task<T> task, TimeSpan timeout)
{var tcs = new TaskCompletionSource<T>();// 设置超时计时器var timer = new Timer(_ => {tcs.TrySetException(new TimeoutException("操作超时"));}, null, timeout, Timeout.InfiniteTimeSpan);task.ContinueWith(t =>{timer.Dispose();if (t.IsFaulted)tcs.TrySetException(t.Exception.InnerExceptions);else if (t.IsCanceled)tcs.TrySetCanceled();elsetcs.TrySetResult(t.Result);});return tcs.Task;
}3. 多个操作竞争
public static Task<T> FirstOf<T>(params Task<T>[] tasks)
{var tcs = new TaskCompletionSource<T>();foreach (var task in tasks){task.ContinueWith(t =>{if (t.Status == TaskStatus.RanToCompletion)tcs.TrySetResult(t.Result);});}return tcs.Task;
}四、重要注意事项
1. 避免重复设置
var tcs = new TaskCompletionSource<int>();// ✅ 正确 - 使用 TrySetResult
tcs.TrySetResult(1);
tcs.TrySetResult(2); // 不会抛异常,但结果仍然是 1// ❌ 错误 - 使用 SetResult 会抛异常
tcs.SetResult(1);
tcs.SetResult(2); // 抛出 InvalidOperationException2. 线程安全考虑
var tcs = new TaskCompletionSource<int>();
object lockObject = new object();
bool isSet = false;void SetResultSafely(int value)
{lock (lockObject){if (!isSet){tcs.SetResult(value);isSet = true;}}
}3. 结合取消令牌
public Task<int> DoWorkAsync(CancellationToken cancellationToken)
{var tcs = new TaskCompletionSource<int>();// 注册取消回调cancellationToken.Register(() =>{tcs.TrySetCanceled(cancellationToken);});Task.Run(() =>{// 模拟工作for (int i = 0; i < 10; i++){cancellationToken.ThrowIfCancellationRequested();Thread.Sleep(100);}tcs.TrySetResult(42);});return tcs.Task;
}五、完整示例
class Program
{static async Task Main(){// 示例1: 基本使用var result = await CreateCompletedTask();Console.WriteLine($"结果: {result}");// 示例2: 异步操作包装var data = await FetchDataAsync();Console.WriteLine($"数据: {data}");}static Task<int> CreateCompletedTask(){var tcs = new TaskCompletionSource<int>();tcs.SetResult(100);return tcs.Task;}static Task<string> FetchDataAsync(){var tcs = new TaskCompletionSource<string>();Task.Run(() =>{try{// 模拟网络请求Thread.Sleep(2000);tcs.SetResult("获取的数据");}catch (Exception ex){tcs.SetException(ex);}});return tcs.Task;}
}六、总结
SetResult用于手动完成任务并设置结果只能调用一次,后续调用会抛出异常
推荐使用
TrySetResult来避免异常适用于包装传统异步模式、实现超时控制等场景
