将 async/await 与 Dispatcher.BeginInvoke() 一起使用

Posted

技术标签:

【中文标题】将 async/await 与 Dispatcher.BeginInvoke() 一起使用【英文标题】:Using async/await with Dispatcher.BeginInvoke() 【发布时间】:2014-06-19 23:42:57 【问题描述】:

我有一个方法,其中包含一些执行await 操作的代码:

public async Task DoSomething()

    var x = await ...;

我需要该代码在 Dispatcher 线程上运行。现在,Dispatcher.BeginInvoke() 可以等待,但我无法将 lambda 标记为 async 以便从其中运行 await,如下所示:

public async Task DoSomething()

    App.Current.Dispatcher.BeginInvoke(async () =>
        
            var x = await ...;
        
    );

在内部async,我得到错误:

无法将 lambda 表达式转换为类型“System.Delegate”,因为它不是委托类型。

如何在Dispatcher.BeginInvoke() 中使用async

【问题讨论】:

【参考方案1】:

other answer 可能引入了一个不为人知的错误。这段代码:

public async Task DoSomething()

    App.Current.Dispatcher.Invoke(async () =>
    
        var x = await ...;
    );

使用Dispatcher.InvokeDispatcher.Invoke(Action callback) 覆盖形式,在这种特殊情况下接受async void lambda。这可能会导致非常意外的行为,因为它通常发生在 async void methods 上。

您可能正在寻找这样的东西:

public async Task<int> DoSomethingWithUIAsync()

    await Task.Delay(100);
    this.Title = "Hello!";
    return 42;


public async Task DoSomething()

    var x = await Application.Current.Dispatcher.Invoke<Task<int>>(
        DoSomethingWithUIAsync);
    Debug.Print(x.ToString()); // prints 42

在这种情况下,Dispatch.Invoke&lt;Task&lt;int&gt;&gt; 接受 Func&lt;Task&lt;int&gt;&gt; 参数并返回相应的可等待的 Task&lt;int&gt;。如果您不需要从DoSomethingWithUIAsync 返回任何内容,只需使用Task 而不是Task&lt;int&gt;

或者,使用Dispatcher.InvokeAsync 方法之一。

【讨论】:

如何将参数传递给“DoSomethingWithUIAsync”? @Cabuxa.Mapache: Dispatcher.Invoke&lt;Task&lt;int&gt;&gt;(() =&gt; DoSomethingWithUIAsync(param)); @Noseratio 当您考虑await Invoke(...) 的工作原理时,很明显它正是想要的。我只需要看到这个例子就可以“得到”它。谢谢! 您链接的关于 Asyc 最佳实践的特别好的 MSDN 文章。 由于没有使用 ConfigureAwait(false),难道 Debug.Print() 调用也不会在 UI 线程上运行吗?【参考方案2】:

使用Dispatcher.Invoke()

public async Task DoSomething()

    App.Current.Dispatcher.Invoke(async () =>
    
        var x = await ...;
    );

【讨论】:

为什么这个答案被否决,谁能解释一下? 接受的答案解释说:“[此表单] 使用 Dispatcher.InvokeDispatcher.Invoke(Action callback) 覆盖形式,在这种特殊情况下接受 async void lambda。这可能会导致非常意外的行为,因为它通常发生在 async void 方法中。" @JonathanGilbert 这种出乎意料的行为到底是什么意思?请提供一些代码。 async void 方法不会返回它已启动的Task(如果有)。调用者实际上甚至无法判断它是否是async。如果您 Dispatcher.InvokeFunc&lt;Task&gt;,那么调度程序会识别此模式并将观察 Task,但如果您将 Dispatcher.Invokeasync void,它实际上运行与同步方法相同的 Dispatcher.Invokeasync 实际上并不是方法签名的一部分;它只是一个返回类型为void 的方法。因此,它被视为同步方法。 接受的答案有一个关于 async/await 最佳实践的页面的链接,其中指出:One subtle trap is passing an async lambda to a method taking an Action parameter; in this case, the async lambda returns void and inherits all the problems of async void methods. As a general rule, async lambdas should only be used if they’re converted to a delegate type that returns Task (for example, Func&lt;Task&gt;).

以上是关于将 async/await 与 Dispatcher.BeginInvoke() 一起使用的主要内容,如果未能解决你的问题,请参考以下文章

将 async/await 与 Dispatcher.BeginInvoke() 一起使用

将 fetch 与 async/await 一起使用会返回 [object Object]

将 Async/Await 与 node-postgres 一起使用

将 Async/await 与 mongoose 一起使用

将 async/await (Swift 5.5) 与 firebase 实时数据库一起使用

Async/Await 与 JQuery 文档 READY