我在等待挂起时遇到问题(描述为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的代码中。

08-17 02:26