将 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.Invoke
的Dispatcher.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<Task<int>>
接受 Func<Task<int>>
参数并返回相应的可等待的 Task<int>
。如果您不需要从DoSomethingWithUIAsync
返回任何内容,只需使用Task
而不是Task<int>
。
或者,使用Dispatcher.InvokeAsync
方法之一。
【讨论】:
如何将参数传递给“DoSomethingWithUIAsync”? @Cabuxa.Mapache:Dispatcher.Invoke<Task<int>>(() => 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.Invoke
的 Dispatcher.Invoke(Action callback)
覆盖形式,在这种特殊情况下接受 async void
lambda。这可能会导致非常意外的行为,因为它通常发生在 async void
方法中。"
@JonathanGilbert 这种出乎意料的行为到底是什么意思?请提供一些代码。
async void
方法不会返回它已启动的Task
(如果有)。调用者实际上甚至无法判断它是否是async
。如果您 Dispatcher.Invoke
和 Func<Task>
,那么调度程序会识别此模式并将观察 Task
,但如果您将 Dispatcher.Invoke
和 async void
,它实际上运行与同步方法相同的 Dispatcher.Invoke
。 async
实际上并不是方法签名的一部分;它只是一个返回类型为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<Task>).
以上是关于将 async/await 与 Dispatcher.BeginInvoke() 一起使用的主要内容,如果未能解决你的问题,请参考以下文章
将 async/await 与 Dispatcher.BeginInvoke() 一起使用
将 fetch 与 async/await 一起使用会返回 [object Object]
将 Async/Await 与 node-postgres 一起使用