WPF Dispatcher.InvokeAsync()与异步委托的奇怪行为

时间:2018-01-21 15:53:57

标签: c# wpf async-await dispatcher

如果你有一个异步方法,如下所示:

    private async Task DoAsync()
    {
        Console.WriteLine(@"(1.1)");
        Thread.Sleep(200);
        Console.WriteLine(@"(1.2)");
        await Task.Delay(1000);
        Console.WriteLine(@"(1.3)");
    }

并且您使用Dispatcher异步调用它:

        Console.WriteLine(@"(1)");
        await Application.Current.Dispatcher.InvokeAsync(DoAsync);
        Console.WriteLine(@"(2)");

您获得的输出将是: (1) (1.1) (1.2) (2) (1.3)

如果您使用Dispatcher.BeginInvoke()并且等待完成事件,效果将是相同的(预期):

        Console.WriteLine(@"(1)");
        var dispatcherOp = Dispatcher.BeginInvoke(new Func<Task>(DoAsync));
        dispatcherOp.Completed += (s, args) =>
        {               
        Console.WriteLine(@"(2)");
        };

我们在这里看到的是DoAsync()方法中的await使得Dispatcher相信操作已经结束。

我的问题:这是一个错误还是一个功能?你知道任何描述这种行为的文件吗?我找不到任何东西。

我没有要求解决方法。

1 个答案:

答案 0 :(得分:6)

这是您代码中的错误,从Application.Current.Dispatcher.InvokeAsync(DoAsync);返回的对象是Task<Task>,您只等待外部任务,而不是等待内部任务完成。这些情况是.Unwrap()的用途。

    Console.WriteLine(@"(1)");
    await Application.Current.Dispatcher.InvokeAsync(DoAsync).Unwrap();
    Console.WriteLine(@"(2)");

您将获得的输出为:(1)(1.1)(1.2)(1.3)(2)

Unwrap正在做的是有效地将您的代码转换为

    Console.WriteLine(@"(1)");
    await (await Application.Current.Dispatcher.InvokeAsync(DoAsync));
    Console.WriteLine(@"(2)");

但是看起来格式更好。

有关文档,请参阅“How to: Unwrap a nested task