如何在需要阻塞时避免异步/等待死锁

时间:2015-06-15 09:13:19

标签: entity-framework

我有以下问题,即EF的SingleOrDefaultAsync无法完成。

我添加了两个额外调用,评估为ab,这两个调用都返回正确的值。但test行永远不会完成,方法只是等待。

任何人都会遇到类似的事情或者有什么可能导致这种情况的提示。

        public async Task<Subscription> FindSubscriptionByIdAsync(Guid subscriptionId)
        {
            //Works
            var a = DbContext.Subscriptions.SingleOrDefault(x => x.Id == subscriptionId);
            //Works
            var b = DbContext.Subscriptions.Include(s => s.StorageLocations).SingleOrDefault(x => x.Id == subscriptionId);

            //No exceptions or anyhting, it just do not continue from this 
            var test =await DbContext.Subscriptions
                .Include(s => s.StorageLocations)
                .SingleOrDefaultAsync(x => x.Id == subscriptionId);
            //Debugger never gets to this point.
            return test;

        }

更新

基于回答/评论我知道我的根本问题是:

    public override CloudStorageAccount GetStorageAccount(string tenant, string purpose = null)
    {
        return GetStorageAccountAsync(new Guid(tenant), purpose).GetAwaiter().GetResult();
    }

我的问题是上面实现了GetStorageAccount,因为它不是返回Task的选项。

由于我拥有接口,我已经更新了我的接口以返回Task,但是如果我无法更新我的设计并被迫返回该对象的无任务版本怎么办? 执行死锁时执行GetStorageAccountAsync()的正确方法是什么?

2 个答案:

答案 0 :(得分:1)

This is the classic ASP.NET await deadlock.你一直在某处调用Wait或Result。不要那样做。

如果您必须阻止(您已指明)以安全的方式阻止:Task.Run(anything).Result。不要修改您等待的所有站点。如果你错过了一个你死锁(可能在晚上4点在生产中不确定)。

在您的示例中,代码为:

Task.Run(() => GetStorageAccountAsync(new Guid(tenant), purpose)).Result

这是安全的。

答案 1 :(得分:0)

我发现如果我不强迫它返回相同的上下文就行了。

    public async Task<Subscription> FindSubscriptionByIdAsync(Guid subscriptionId)
    {

        return await DbContext.Subscriptions
            .Include(s => s.StorageLocations)
            .SingleOrDefaultAsync(x => x.Id == subscriptionId)
            .NotOnCapturedContext();

    }

NotOnCapturedContext是以下扩展方法。

namespace System.Threading.Tasks
{
    using System.Runtime.CompilerServices;

    public static class TaskExtensions
    {
        public static ConfiguredTaskAwaitable NotOnCapturedContext(this Task task)
        {
            return task.ConfigureAwait(false);
        }

        public static ConfiguredTaskAwaitable<T> NotOnCapturedContext<T>(this Task<T> task)
        {
            return task.ConfigureAwait(false);
        }
    }
}