繁体   English   中英

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

[英]Set SynchronizationContext to null instead of using ConfigureAwait(false)

我有一个公开方法的同步和异步版本的库,但在幕后,它们都必须调用异步方法。 我无法控制异步方法(它采用异步/ 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);
}

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

(MyMethodAsync 完全不知道它是在 ASP .NET 上下文中运行的,它不会调用HttpContext.Current或类似的东西。它只是执行一些异步 SQL 调用,并且作者没有将ConfigureAwait(false)在其中任何一个上)

我有一个公开方法的同步和异步版本的库,但在幕后,它们都必须调用异步方法。

该库公开同步版本是错误的 假设同步 API 不存在。

所以为了避免死锁

如果您调用使用async / await的异步方法,死锁应该不会有任何问题。 如果它不使用ConfigureAwait(false) ,那么它的效率就不会那么高,仅此而已。 由于ConfigureAwait(false)导致的死锁仅在您尝试执行异步同步时适用(即,如果您从该库调用同步 API)。

因此,最简单和最简单的解决方案是忽略同步 API,它们无论如何都是错误设计的:

return await MyMethodAsync();

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

Task.Run有很多问题:

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

我也理解在不惜一切代价避免阻塞异步代码的情况下提出任何建议时犹豫不决,但是您已经明确表示您知道这一点,这是为了解决您无法直接控制的已知情况。 我认为对这个话题的教条态度正在损害 .NET 生态系统。

SynchronizationContext设置为 null 对我来说似乎很棘手。 相反,您可以真正将工作委托给线程池。 使用Task.Run ..

var result = Task.Run(() => MyMethodAsync()).Result;

或者

var result = Task.Run(async () => await MyMethodAsync()).Result;

这避免了死锁并消除了hacky代码。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM