【问题标题】:Deadlock on Linq to Entity Framework Core 2.0 TableLinq to Entity Framework Core 2.0 表上的死锁
【发布时间】:2018-07-10 23:38:54
【问题描述】:

我在尝试并行运行一堆 linq 查询时遇到了我认为的死锁。

我在这个方法上运行Task.WhenAll()

public async Task<MetabuildScan> GetLatestMetabuildScanAsync(string buildId)
{
    var metabuildScanStatuses = new[] { "Completed", "Referenced" };

    // Get the latest metabuild scan for this image build
    var latestScan = await (from scan in _qsaContext.MetabuildScans
                           where scan.SoftwareImageBuildId == buildId
                           && metabuildScanStatuses.Contains(scan.SIScanStatus)
                           orderby scan.SIScanStartedOn descending
                           select scan).FirstOrDefaultAsync();

    // If there is a related scan, then use that one, else, use the one we just got
    var latestCompletedScanId = latestScan?.RelatedScanId ?? latestScan?.Id;

    return await _qsaContext.MetabuildScans
                 .FirstOrDefaultAsync(scan => scan.Id == latestCompletedScanId);
}

我收到了System.InvalidOperationException: A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.

_qsaContext 是使用 Entity-Framework Core 创建的。

起初,我认为FirstOrDefaultAsync 可以解决我的问题(我一开始有一个非异步的FirstOrDefault),但它没有。

我想知道解决这个僵局的最佳解决方案是什么。我从中选择的表是一个大表,所以我无法将整个表拉到内存中。

【问题讨论】:

    标签: linq asynchronous async-await entity-framework-core


    【解决方案1】:

    像往常一样,将您的查询包装在 try/catch 中并重复您的事务。

    【讨论】:

    • 捕获了异常,但并没有解决我的问题。更新有例外的答案,希望对您有所帮助。
    • 有时在大型多线程/多进程系统中很难避免死锁。捕获并重试是最简单的解决方案。另一种方法是使用 NOLOCK。
    • 如果捕获和重试是唯一的解决方案,那么让这段代码异步是没有意义的。捕获和重试也会增加所有重试尝试的大量开销。我正在使用的这种方法几乎总是同时在 10 个不同的线程上运行。这是很多开销。
    • 带有 LINQ 的 NOLOCK 是一个好的解决方案吗?我见过stackoverflow.com/questions/1220807/nolock-with-linq-to-sql,但解决方案看起来并不干净,它们看起来像黑客。
    • 数据库不关心同步/异步调用。它收到请求并且刚刚发生了死锁。如果您有很多线程并且您希望您的操作系统一次启动/执行/完成更多线程,那么异步是正确的方法。 Try/catch 不会为您的代码增加任何额外的昂贵开销。对于数据可能快速变化的银行来说,重复交易可能不是一个好的解决方案。在您的情况下,您使用存储在表中的一些队列。所以,应该没问题。
    【解决方案2】:

    Entity Framework DbContext 不是线程安全的。你不能像你试图做的那样对它运行并行查询。

    如果您需要为您的查询提供一个共享上下文,您需要单独并按顺序等待每个查询。

    如果它们不需要共享上下文但确实需要并行运行,那么您需要为每个查询提供单独的上下文。

    如果使用 DI 框架,也许您可​​以考虑让您的 DbContext Transient(而不是我假设的 Scoped)并将其注入到将调用您的查询方法的查询类中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-02-23
      • 1970-01-01
      • 1970-01-01
      • 2021-07-12
      • 1970-01-01
      • 2022-08-20
      • 1970-01-01
      相关资源
      最近更新 更多