You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在WinUI的DispatcherQueue中执行异步代码且不使用async void?

不使用async void实现DispatcherQueue异步主线程执行的最佳方式

核心思路

通过TaskCompletionSource桥接DispatcherQueue.TryEnqueue的同步委托要求与异步逻辑,将异步逻辑封装在async Task方法中,避免直接使用async void委托,同时保证异常能正确传递给调用者,遵循微软的异步线程规则。

改进后的实现代码

public static async Task Execute(this DispatcherQueue dispatcher, Func<Task> function)
{
    // 已在主线程,直接执行异步方法
    if (dispatcher.HasThreadAccess)
    {
        await function();
        return;
    }

    // 使用RunContinuationsAsynchronously避免续延在Dispatcher线程同步执行引发死锁
    var taskCompletionSource = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
    
    // 传入同步Action,内部调用异步处理方法,避免async void
    var enqueued = dispatcher.TryEnqueue(() =>
    {
        // 启动异步任务,无需等待(由TCS跟踪完成状态)
        _ = CompleteTask(function, taskCompletionSource);
    });

    if (!enqueued)
    {
        // 入队失败时主动设置异常
        taskCompletionSource.SetException(new InvalidOperationException("无法将任务排入DispatcherQueue"));
    }

    // 等待异步任务完成
    await taskCompletionSource.Task;
}

// 封装异步逻辑的async Task方法,避免async void
private static async Task CompleteTask(Func<Task> function, TaskCompletionSource<bool> tcs)
{
    try
    {
        await function();
        // 尝试设置完成状态,防止TCS已被处置
        tcs.TrySetResult(true);
    }
    catch (Exception ex)
    {
        // 传递异常给调用者
        tcs.TrySetException(ex);
    }
}

方案优势

  • 避免async void风险:传入TryEnqueue的是同步Action,内部调用的是async Task方法,不会产生async void委托,彻底规避了其导致的进程崩溃、无法追踪异常等问题
  • 异常可控传递:所有异常都会被捕获并通过TaskCompletionSource传递给调用者,调用者可通过await或Task.Exception处理异常,支持遥测系统上报
  • 支持任务等待:调用者可await返回的Task,VS包在Package.Close时能正常等待该异步工作完成
  • 死锁防护:TaskCreationOptions.RunContinuationsAsynchronously确保TCS的续延不会在Dispatcher线程同步执行,避免潜在死锁

补充说明(翻译自原提问)

我了解DispatcherQueue.TryEnqueue的重载方法接受的是不返回Task的Action或Func参数。我的问题在于如何弥合同步到异步的间隙,同时遵循微软的线程规则第4条:永远不要定义async void方法,应改为返回Task。该规则指出:

  • async void方法抛出的异常会直接导致进程崩溃
  • 调用者无法选择await结果
  • 调用者无法将异常上报至遥测系统
  • 若以此方式启动异步工作,VS包无法在Package.Close时等待工作完成
  • 注意:当异步委托或async () =>被传入接受Action委托的方法时,会成为async void方法,仅应将异步委托传入接受Func或Func<Task>参数的方法

内容的提问来源于stack exchange,提问作者Batesias

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 12:35:37