c# TaskCompletionSource 的作用 c#怎么手动控制Task

TaskCompletionSource的核心作用是手动创建可await的Task,通过SetResult/SetException/SetCanceled控制其完成状态,实现回调到async/await的轻量桥接。

c# taskcompletionsource 的作用 c#怎么手动控制task

TaskCompletionSource 的核心作用:手动捏造一个可 await 的 Task

它不执行任何异步逻辑,也不启动线程——它只是给你一个 Task 实例的“遥控器”。你用 TaskCompletionSource 创建出一个未完成的 Task,然后在任意时机(比如事件回调、第三方 SDK 通知、UI 线程响应后)调用 SetResult()SetException()SetCanceled(),来强行“推”这个 Task 进入终态。这是把“回调驱动”转成“async/await 驱动”的最轻量级桥梁。

怎么手动控制 Task:三步走,缺一不可

常见错误是只创建了 TaskCompletionSource 却忘了暴露它的 Task,或者在多线程环境下没注意线程安全调用 Set* 方法(虽然 Set* 本身是线程安全的,但业务逻辑可能不是)。

  • 第一步:创建实例,声明你要返回的类型,比如 new TaskCompletionSource()new TaskCompletionSource()
  • 第二步:把 tcs.Task 返回出去,供调用方 await;别直接 await tcs ——它不是 Task
  • 第三步:在真正该结束的时候(例如按钮点击、WebSocket 收到响应、Timer 触发),调用对应方法:
     • 成功 → tcs.SetResult("done")
     • 失败 → tcs.SetException(new InvalidOperationException("timeout"))
     • 取消 → tcs.SetCanceled()(注意:这会触发 OperationCanceledException

典型场景:包装 UI 弹窗、事件、老式 Begin/End 模式

比如你在 WPF 或 MAUI 中弹登录框,不能直接 await ShowDialog()(它同步阻塞)。这时就用 TaskCompletionSource 桥接:

private TaskCompletionSource _loginTcs;

public async Task ShowLoginAsync()
{
    _loginTcs = new TaskCompletionSource();
    var window = new LoginWindow();
    window.LoginCompleted += (result) => _loginTcs.SetResult(result); // 事件回调里推进
    window.Show();
    return await _loginTcs.Task;
}

⚠️ 注意:如果用户关掉窗口没触发事件,_loginTcs.Task 就永远挂起——必须配超时或取消逻辑,否则会内存泄漏+死等。

微信 WeLM

微信 WeLM

WeLM不是一个直接的对话机器人,而是一个补全用户输入信息的生成模型。

下载

容易踩的坑:重复 Set / 忘记 Set / 线程错乱

TaskCompletionSource 是一次性状态机:一旦调用了 SetResult,再调一次就会抛 InvalidOperationException: "The task has already been completed."。这不是 bug,是设计使然。

  • 别在多个地方无保护地调用 Set* ——加 Interlocked.CompareExchange 或用 if (tcs.TrySetResult(...)) 更安全(TrySet* 系列方法会静默失败,适合竞态场景)
  • 别漏掉异常路径:比如网络请求超时、事件没订阅成功、回调被 GC 掉,都可能导致 Task 永远不完成
  • 不要在非 UI 线程直接操作 WPF/WinForms 控件后再调 SetResult ——先 Dispatcher.InvokeBeginInvoke 回 UI 线程,再 Set

最常被忽略的一点:它和 CancellationToken 没有自动绑定。你想支持取消,得自己监听 token 并在 token.Register(() => tcs.TrySetCanceled()),而不是指望 TaskCompletionSource 自动感知。

https://www.php.cn/faq/2034618.html

发表回复

Your email address will not be published. Required fields are marked *