这是一个经典的僵局。 UI 正在等待异步方法完成,但异步方法尝试更新 UI 线程并轰隆隆,陷入僵局。
但奇怪的是,如果我将这个模式或多或少地逐字复制到
控制台应用程序,它可以工作。
那是因为你的 WinForm 应用程序有一个自定义的SynchronizationContext
。它是隐式捕获的,它的工作是在从您的应用程序返回后将工作编组回 UI 线程。await
.
您真的应该公开异步操作的同步包装器吗? http://blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx, 答案是no.
有一个解决办法,但我不太喜欢它。如果你绝对have to(你不)同步调用你的代码(同样,你真的不应该),使用ConfigureAwait(false)
在异步方法内部。这指示awaitable
不捕获当前的同步上下文,因此它不会将工作重新编组到 UI 线程上:
public async Task<string> MyMethodAsync()
{
using (var cl = new HttpClient())
{
return await cl.GetStringAsync("http://www.google.co.uk/")
.ConfigureAwait(false);
}
}
请注意,如果您执行此操作,然后尝试调用任何 UI 元素,您最终会得到一个InvalidOperationException
因为你不会在 UI 线程上。
通过构造函数初始化 UI 是一种常见模式。 Stephan Cleary 有一个非常好的关于异步的系列,你可以找到here http://blog.stephencleary.com/2013/01/async-oop-2-constructors.html.
我究竟做错了什么?这是否只是一个糟糕的模式?如果是,那么是什么?
我应该使用模式吗?
是的,一点没错。如果您想公开异步和同步 API,请使用正确的 api,这不会让您在第一种情况下陷入这种情况(死锁)。例如,如果您想公开一个同步DownloadString
, use WebClient http://msdn.microsoft.com/en-us/library/system.net.webclient(v=vs.110).aspx反而。