【问题标题】:Using async/await with Dispatcher.BeginInvoke()将 async/await 与 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

【问题讨论】:

    标签: c# wpf asynchronous lambda async-await


    【解决方案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;).
    猜你喜欢
    • 2017-11-05
    • 2019-04-15
    • 2018-01-14
    • 2018-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多