我在等待挂起时遇到问题(描述为here)。在研究过程中,我发现在SetResult
上调用TaskCompletionSource
实际上会在名为SetResult
的线程的上下文中调用等待继续(在this answer中也对某个相关问题进行了说明)。在我的情况下,这是与启动等待(ASP.NET请求线程)的线程不同的线程(线程池工作线程)。
尽管我仍然不确定为什么会导致挂起,但我还是决定尝试将SetResult
强制进入原始上下文。我在请求线程上输入await之前存储了SynchronizationContext.Current
的值,并在调用SynchronizationContext.SetSynchronizationContext
之前通过SetResult
将其手动应用于工作线程中。这样就解决了挂起问题,现在我可以等待所有异步方法,而不必指定ConfigureAwait(false)
。
我的问题是:这是手动捕获和应用SynchronizationContext
的合理且正确的方法吗? FWIW,我尝试首先使用Post()
委托执行简单的SetResult
,但这仍然导致挂起。我在这里显然超出了我的舒适范围……请帮助我了解发生了什么!
最佳答案
SetResult
不保证可以调用任何东西。因此,这是不可靠的。
您需要在捕获同步上下文的位置进行切换。常见的痛点是WebClient
,它在启动Web请求时捕获上下文。因此,您的代码如下所示:
SetContext(newContext);
new WebClient().DownloadAsync(...);
SetContext(oldContext);
恢复旧的上下文以不打扰任何事情。
换句话说,问题出在继续代码中,而不是在调用
SetResult
的代码中。