我遇到悬挂等待的问题(描述here)。在研究期间,我发现在SetResult
上调用TaskCompletionSource
实际上在调用SetResult
的线程的上下文中调用等待继续(这也在this answer中拼写为a有点相关的问题)。在我的例子中,这是一个与启动await(ASP.NET请求线程)的线程不同的线程(线程池工作线程)。
虽然我仍然不确定为什么会导致挂起,但我决定尝试将SetResult
强制转换为原始上下文。我在请求线程上输入await之前存储了SynchronizationContext.Current
的值,并在调用SynchronizationContext.SetSynchronizationContext
之前通过SetResult
在工作线程中手动应用它。这解决了问题,我现在可以等待所有异步方法,而无需指定ConfigureAwait(false)
。
我的问题是:这是一种合理而正确的方法来手动捕获和应用SynchronizationContext
吗? FWIW,我尝试首先使用Post()
委托做一个简单的SetResult
,但这仍然导致挂起。我在这里显然有点偏离了我的舒适区...请帮助我理解发生了什么!
答案 0 :(得分:3)
SetResult
无法保证可以拨打任何电话。因此,这不可靠。
您需要在捕获它的位置切换同步上下文。这里常见的痛点是WebClient
,它在启动Web请求时捕获上下文。所以你的代码看起来像这样:
SetContext(newContext);
new WebClient().DownloadAsync(...);
SetContext(oldContext);
恢复旧的上下文以免打扰任何事情。
换句话说,问题在于延续代码,而不是在调用SetResult
的代码中。
答案 1 :(得分:0)
令我尴尬的是,我完全忽略了我的HTTP处理程序是从一个小的基类派生的,它以非常可疑的方式实现了IAsyncHttpHandler
,以便添加对异步处理程序的支持:
public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
{
...
var task = HandleRequestAsync(...);
Task.Run(async () => { await task; }).GetAwaiter().GetResult();
...
}
我甚至不记得为什么我这样做了(这是一年多以前),但它肯定是 THE 愚蠢的部分,我一直在寻找最后一对几天!
将处理程序基类更改为.NET 4.6' s HttpTaskAsyncHandler
摆脱了挂起。对不起浪费大家的时间! :(