通过Dispatcher调用异步lambda时双重等待

时间:2014-07-13 01:18:31

标签: c# wpf task-parallel-library async-await dispatcher

private async Task<T> LoadForm(WebControlAsync browser, Uri url)
{ ... }

var forms = await await _dispatcher.InvokeAsync(async () => await LoadForm(browser, form.Url));

我不明白为什么我必须在await使用两个T来获取forms中的InvokeAsync?因此看起来Task<Task<T>>会返回var forms = await _dispatcher.InvokeAsync(() => FillForm(forms, url)); 。但是当我调用这样的同步方法时:

await

它只需要一个var forms = await await _dispatcher.InvokeAsync(() => LoadForm(browser, form.Url)); 。所以原因似乎是异步lambda。我明白,如果我这样写:

LoadForm

然后Task<T>的返回类型为InvokeAsyncTask<lambda return type>返回Task<Task<T>>,因此它确实为Task。但是当await方法被Task编辑时,是不是从var forms = await LoadForm(browser, form.Url); “解开”了实际的返回类型?所以,如果我写:

forms

T将是Task<T>,而不是{{1}}。为什么在async lambda中没有发生同样的情况?

1 个答案:

答案 0 :(得分:8)

你已经回答了自己的问题。 InvokeAsync返回Task<T>,其中T是提供给它的Func<TResult>委托的返回类型。当您使用异步lambda时,您不再处理Func<TResult>,而是处理Func<Task<TResult>>

您认为展开应该自动发生的原因可能是由于过去使用Task.Run。但应注意,Task.Run的重载接受Func<T>并返回Task<T>,重载接受Func<Task<T>>,而仍然会返回Task<T>,因此您可以将此问题展开给幕后发生的事情。但是,这不适用于您的情况。

在这方面,

Dispatcher.InvokeAsyncTask.Factory.StartNew类似。它没有专门处理Func<Task<TResult>>的重载,所以你不得不“手动”解包。

事情是,考虑一下你的场景中是否真的需要InvokeAsync。除了调度程序线程超负荷工作之外,它不会给你太多。它返回的Task通常会立即完成(无需等待Task lambda创建的async),只需再给你一层包装来处理。 LoadForm已经async,所以您不妨使用Dispatcher.Invokeasync SynchronizationContext lambda,并最终返回您的任务LoadForm想要等待,或者,如果您知道将始终使用正确的SynchronizationContext(即在UI线程上)调用您的async/await,请完全省略调度程序调用,并让{{1}做它的事。