【问题标题】:What is causing this particular method to deadlock?是什么导致这种特定方法死锁?
【发布时间】:2014-08-13 05:32:47
【问题描述】:

尽我所能,我一直选择异步。但是,我仍然坚持使用不是为异步构建的 ASP.NET Membership。因此,我对 string[] GetRolesForUser() 等方法的调用无法使用异步。

为了正确构建角色,我依赖来自各种来源的数据,因此我使用多个任务并行获取数据:

public override string[] GetRolesForUser(string username) {
   ...
   Task.WaitAll(taskAccounts, taskContracts, taskOtherContracts, taskMoreContracts, taskSomeProduct);
   ...
}

所有这些任务都只是使用实体框架从 SQL Server 数据库中获取数据。但是,最后一个任务 (taskSomeProduct) 的引入导致了死锁,而其他方法都没有。

下面是导致死锁的方法:

public async Task<int> SomeProduct(IEnumerable<string> ids) {
    var q = from c in this.context.Contracts

            join p in this.context.Products
            on c.ProductId equals p.Id

            where ids.Contains(c.Id)

            select p.Code;

    //Adding .ConfigureAwait(false) fixes the problem here
    var codes = await q.ToListAsync();
    var slotCount = codes .Sum(p => char.GetNumericValue(p, p.Length - 1));

    return Convert.ToInt32(slotCount);
}

但是,此方法(看起来与所有其他方法非常相似)不会导致死锁:

public async Task<List<CustomAccount>> SomeAccounts(IEnumerable<string> ids) {
    return await this.context.Accounts
        .Where(o => ids.Contains(o.Id))
        .ToListAsync()
        .ToCustomAccountListAsync();
}

我不太确定导致死锁的方法是什么。最终,他们都在做同样的查询数据库的任务。将ConfigureAwait(false) 添加到一种方法确实可以解决问题,但我不太确定它与其他执行良好的方法有什么区别。

编辑

为了简洁起见,我最初省略了一些附加代码:

public static Task<List<CustomAccount>> ToCustomAccountListAsync(this Task<List<Account>> sqlObjectsTask) {
    var sqlObjects = sqlObjectsTask.Result;
    var customObjects = sqlObjects.Select(o => PopulateCustomAccount(o)).ToList();
    return Task.FromResult<List<CustomAccount>>(customObjects);
}

PopulateCustomAccount 方法只是从数据库Account 对象返回一个CustomAccount 对象。

【问题讨论】:

  • @MarcelN。 - 这个问题特别说要使用await,正如我在开篇中所说的那样,我不能使用它。这个问题与您链接到的问题不同,因为我的问题仅出现在正在使用的许多特定 Task 中。我想知道导致问题的特定方法是什么,而其他方法都可以。
  • @MarcelN。 - 是的,我在所有任务中使用相同的上下文。实际上我最初使用的是WhenAll,但这也陷入了僵局。该返回类型上没有 Result 属性。
  • 我确定 Entity Framework 不支持在同一上下文中并行执行等待功能。事实上,它应该抛出一个异常。
  • @JustinHelgerson EF 并没有突然放弃并发支持。它从来不在那里。您以并发方式使用 EF 是无效的,必须修复。您寻求解释或解决方案吗?解决方案很简单:使用 ConfigureAsync。解释:我有预感。将 await Task.Delay(100); 添加到 SomeAccounts 并查看它现在是否死锁。它应该。并发布 ToCustomAccountListAsync 的定义。

标签: c# asp.net asynchronous async-await deadlock


【解决方案1】:

ToCustomAccountListAsync 中,您调用Task.ResultThat's a classic deadlock. 使用await

【讨论】:

    【解决方案2】:

    这不是一个答案,但我有很多话要说,它不适合 cmets。

    一些事实: EF 上下文不是线程安全的,不支持并行执行:

    虽然线程安全会使异步更有用,但它是一个正交特性。鉴于 EF 与由用户代码组成的图形交互以维护状态并且没有简单的方法来确保此代码也是线程安全的,因此尚不清楚我们是否可以在最一般的情况下实现对它的支持。

    目前,EF 将检测开发人员是否尝试同时执行两个异步操作并抛出。

    一些预测: 你这么说:

    其他四个任务的并行执行已经在生产中运行了几个月,没有死锁。

    它们不能并行执行。一种可能性是线程池不能为您的操作分配多个线程,在这种情况下,它们将按顺序执行。或者这可能是您初始化任务的方式,我不确定。假设它们是按顺序执行的(否则你会认出我正在谈论的异常),还有另一个问题:

    Task.WaitAll hanging with multiple awaitable tasks in ASP.NET

    所以也许它不是关于那个特定的任务SomeProduct,但它总是发生在最后一个任务上?好吧,如果它们并行执行,则不会有“最后一个任务”,但正如我已经指出的那样,考虑到它们已经投入生产很长时间,它们必须按顺序运行。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-12-27
      • 2017-12-31
      • 2012-01-16
      • 2021-07-13
      • 1970-01-01
      • 1970-01-01
      • 2012-08-27
      • 1970-01-01
      相关资源
      最近更新 更多