将SynchronizationContext设置为null而不是使用ConfigureAwait(false)

Rya*_*yan 12 c# asp.net async-await

我有一个库,它公开了方法的同步和异步版本,但是在引擎盖下,它们都必须调用异步方法.我无法控制异步方法(它采用异步/等待和不使用ConfigureAwait(false)),我也不能更换.

代码在ASP .NET请求的上下文中执行,所以为了避免死锁,这就是我所做的:

var capturedContext = SynchronizationContext.Current;
try
{
    // Wipe the sync context, so that the bad library code won't find it
    // That way, we avoid the deadlock
    SynchronizationContext.SetSynchronizationContext(null);

    // Call the async method and wait for the result
    var task = MyMethodAsync();
    task.Wait();

    // Return the result
    return task.Result;
}
finally
{
    // Restore the sync context
    SynchronizationContext.SetSynchronizationContext(capturedContext);
}
Run Code Online (Sandbox Code Playgroud)

这是否产生与MyMethodAsync在ConfigureAwait(false)其所有await上使用的效果相同的效果?我忽略了这种方法还有其他一些问题吗?

(MyMethodAsync完全没有意识到它是在ASP .NET上下文中运行的,它不会执行任何调用HttpContext.Current或类似的任何操作.它只是执行一些异步SQL调用,而编写器没有放置ConfigureAwait(false)任何这些调用)

Stu*_*art 6

如果您将此技术包装在适当命名的静态函数中,我认为您的建议明显优于Task.Run,即使仍然是两害相权取其轻。

Task.Run有很多问题:

  • 目前尚不清楚您为什么要使用它,您想在网络服务器上启动新任务吗?如果没有评论,新开发者很快就会删除。然后繁荣,难以诊断生产问题(死锁)。
  • 当不需要时,它会在新的线程池线程上启动,直到到达第一个等待完成的延续。
  • 它使您同步阻塞整个任务返回函数,从您对问题的描述来看,阻塞实际上只是整个任务的一部分。这里鼓励的是对异步代码进行更长的阻塞,这当然不是您想要的。
  • 如果你在多个级别上使用它,你就会使问题成倍增加(SetSynchronizationContext多次使用并没有坏处)。
  • 如果事实证明您认为没有阻塞/死锁,或者它已经被修复,那么Task.Run现在正在引入异步阻塞,而SetSynchronizationContext除了通过不恢复上下文而进行的优化之外,不会给您带来任何成本不断地。

我也理解,鉴于应不惜一切代价避免阻塞异步代码,因此对于提出任何建议都会犹豫不决,但是您已经明确表示您已经意识到这一点,这是为了修复您直接控制范围之外的已知情况。我认为对这个主题的教条态度正在损害 .NET 生态系统。


Ste*_*ary 5

我有一个库,它公开了方法的同步和异步版本,但是在引擎盖下,它们都必须调用异步方法.

公开同步版本库是错误的.只是假装同步API不存在.

所以要避免死锁

如果调用使用async/ 的异步方法,则死锁应该没有任何问题await.如果它不使用ConfigureAwait(false),那么它就没有那么高效,就是这样.死锁ConfigureAwait(false)仅适用于您尝试执行异步同步时(即,如果您从该库调用同步API).

因此,最简单和最简单的解决方案是忽略同步API,这些API设计不正确:

return await MyMethodAsync();
Run Code Online (Sandbox Code Playgroud)

  • 您正在使用的库是要求代码异步的库; 我的第一个问题是为什么图书馆仍然没有遗产?但是如果你坚持异步同步(这将影响ASP.NET的可伸缩性),那么要么`Task.Run(...).Result`或者暂时替换`SynchronizationContext`(我会在`using `而不是`finally`)会起作用.我倾向于'Task.Run`以获得代码清晰度,即使效率稍差. (3认同)
  • 甚至Stephen Toub也承认有时你确实需要"同步异步"."我根本无法强制要求所有其他代码(尤其是遗留代码)都是异步的; 我必须提供同步版本.值得一提的是,异步函数调用仅在某些情况下发生.大部分功能确实是真正的同步; 我只想处理这个角落案件. (2认同)