异步/等待片段在针对MS-SQL-Server时会出现死锁(预期),但针对Oracle时不会出现死锁(意外)。

3

前置警告:

我十分清楚在下面所示的上下文中使用.Result是绝对错误的,应该尽一切可能避免。我知道正确的方法是什么。

这个问题的本质更多地与db驱动程序的基础设施有关:Oracle vs Ms-Sql-Server。这在很大程度上是一个学术问题。


System.Web.MVC.dll: ver. 5.2.3.0 (MVC5)
EntityFramework.dll: ver. 6.1.3
EntityFramework.SqlServer.dll: ver. 6.1.3
Oracle.ManagedDataAccess: ver. 4.121.2.0 (oracle driver ver 12.2.0.1.0)
Oracle.ManagedDataAccess.EntityFramework: ver. 6.121.2.0  (oracle driver ver 12.2.0.1.0)

考虑以下存储库及其在ASP.NET MVC控制器中的调用:

考虑以下存储库及其在ASP.NET MVC控制器中的调用:

 public class MyRepository {
     [...]
     public async Task<SomeEntity> GetFirstFooAsync() => await new SomeEFContext().FooTable.FirstOrDefaultAsync();
 }

 public class SomeController : Controller {
     public ActionResult SomeAction() {
          var result = new MyRepository.GetFirstFooAsync().Result; //<-- crappy approach I know
          [...]
     }
 }

上述代码片段在底层数据库为Oracle时不会死锁。但是,当使用MS提供的EF关联驱动程序连接到MS-Sql-Server时,这段完全相同的代码会导致死锁。
死锁是此类糟糕代码的预期行为。
  • 为什么Oracle驱动程序不会引起死锁?它是否在内部使用了.ConfigureAwait(false)或其他调整方式?
  • 如果Oracle驱动程序确实使用了优化,那么即使适当地编写调用,我也应该担心它可能具有不良副作用吗?
如下所示:
 public class SomeController : Controller {
     public async Task<ActionResult> SomeAction() {
          var result = await new MyRepository.GetFirstFooAsync();
          [...]
     }
 }

我之所以问这个问题,是因为我认为互联网智慧反对部分使用 .ConfigureAwait(false),因此即使它们针对其他组件,我也应在所有其他异步调用中使用 .ConfigureAwait(false)。然而,我不确定我是否正确理解/解释了上述建议 - 如果我有疑问,请指出我的错误。如果我正确关注,请您提供一个示例,其中部分使用 .ConfigureAwait(false) 将会导致问题,并且提供多个 Async() 调用时如何使用 .ConfigureAwait(false) 的示例。


当您通过最后的代码示例正确实现异步时,Oracle 会如何响应? - Erik Philips
表面上看,“async/await all the way”方法在Oracle和MS-SQL-Server上都可以正常工作。我之所以倾向于问这个问题,是因为它在Oracle下工作并不意味着一切都没问题。异步/等待代码有许多令人讨厌的问题,如果底层第三方代码(这里是通过Async()访问的Oracle ef-driver)没有100%正确实现,可能会导致死锁。 - XDS
我强烈建议一直使用async/await,直到你真正遇到问题。 - Erik Philips
不用说,我遵循“全程异步/等待”的方式。就个人而言,我非常烦恼的是,我确信 Oracle 驱动程序的实现存在问题,而我必须等待在生产环境中以一种难以追踪和模糊的方式出现故障才能采取行动。异步/等待基础设施不应该导致这样的失眠效应。 - XDS
为什么它在生产环境中会出现问题,而在开发人员、QA或任何进入生产之前的流程中却没有问题?这听起来像是一个流程问题。 - Erik Philips
2
实际上,QA 很少能够确定这些微妙的细节是否会导致边缘情况下的 bug。我很钦佩那些开发人员,他们可以把手放在火上,并发誓这样的代码将在生产环境中按预期工作,即使涉及可疑行为的第三方组件(在这种情况下是 Oracle)。就个人而言,我发现更有益的是担心那些表现出异常迹象的组件是否值得信任。但这只是我的看法 :) - XDS
2个回答

0

https://community.oracle.com/thread/4092961

糟糕了。Oracle让我们很头疼。他们对async/await没有任何尊重。他们内部会使用阻塞调用,好像根本没有使用async/await一样。真是“太好了”。


0

ConfigureAwait(false) 无法防止死锁。

我猜 Oracle 可能是在 Task.Run(或类似的东西)中包装异步代码。

在 ASP.NET Core 中不需要使用 ConfigureAwait(false)(因为它没有同步上下文),并且不应在操作方法内部使用,因为在退出操作方法后仍需要上下文。

你可以尝试这个方法(我没有尝试过),看看它是否适用于两个提供程序:

public class SomeController : Controller
{
    public ActionResult SomeAction()
    {
        var synchronizationContext = SynchronizationContext.Current;
        try
        {
            var result = new MyRepository.GetFirstFooAsync().Result;
            [...]
        }
        finally
        {
            SynchronizationContext.SetSynchronizationContext(synchronizationContext);
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接