导致这种特殊方法死锁的原因是什么?

时间:2014-08-11 21:44:09

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

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

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

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

所有这些任务都只是使用Entity Framework从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方法只返回数据库CustomAccount对象中的Account对象。

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:0)

这不是答案,但我有很多话要说,它不适合评论。

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

  

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

     

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

有些预测: 你这么说:

  

其他四项任务的并行执行已经生效数月而没有死锁。

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

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

所以也许它不是关于那个特定的任务SomeProduct,但它总是发生在最后一个任务上?好吧,如果他们并行执行,那么最后一项任务就不会是&#34;但正如我已经指出的那样,考虑到它们已经生产了相当长的时间,它们必须按顺序运行。