在ASP.NET的上下文中,为什么不调用异步方法时Task.Run(...).结果死锁?

aho*_*mes 5 c# asp.net asynchronous task synchronizationcontext

我用一个控制器和一个方法创建了一个简单的WebApi项目:

public static class DoIt
{
    public static async Task<string> GetStrAsync(Uri uri)
    {
        using (var client = new HttpClient())
        {
            var str = await client.GetStringAsync(uri);
            return str;
        }
    }
}

public class TaskRunResultController : ApiController
{
    public string Get()
    {
        var task = Task.Run(() =>
            DoIt.GetStrAsync(new Uri("http://google.com"))
        );
        var result = task.Result;

        return result;
    }
}
Run Code Online (Sandbox Code Playgroud)

我非常了解async/await和tasks; 斯蒂芬克莱里几乎虔诚地跟随他.只是存在.Result让我焦虑,我希望这会陷入僵局.我知道这Task.Run(...)很浪费,导致在等待异步DoIt()完成时占用一个线程.

问题是这不是死锁,而是让我心悸.

我看到一些答案,如/sf/answers/2282496401/,我也观察到SynchronizationContext.Currentlambda执行时为null.但是,我也有类似的问题,问为什么上面的代码会出现死锁,而且我发现死锁ConfigureAwait(false)是在使用(不捕获上下文)的情况下发生的.Result.

是什么赋予了?

Ste*_*ary 7

Result本身不会导致僵局.死锁是指代码的两个部分都在等待彼此.A Result只是一次等待,因此它可能是死锁的一部分,但它并不总是导致死锁.

标准示例中,Result等待任务在保持上下文时完成,但任务无法完成,因为它正在等待上下文自由.所以有一个僵局 - 他们正在等待对方.

ASP.NET Core根本没有上下文,所以Result不会死锁.(由于其他原因,它仍然不是一个好主意,但它不会死锁).

问题是这不是死锁,而是让我心悸.

这是因为该Task.Run任务不需要完成上下文.所以打电话Task.Run(...).Result是安全的.在Result等待任务完成,并持有到上下文(防止请求的任何其他地方在这方面执行),但没关系,因为Task.Run任务并不需要上下文的.

Task.Run对于ASP.NET(由于其他原因),一般来说仍然不是一个好主意,但这是一种非常有用的技术,不时有用.它不会死锁,因为Task.Run任务不需要上下文.

但是,我也有类似的问题,问为什么上面的代码会出现死锁,

相似但不完全相同.仔细查看这些问题中的每个代码语句,并问自己它运行的上下文.

我已经看到在与.Result一起使用ConfigureAwait(false)(不捕获上下文)的情况下会发生死锁.

Result/上下文僵局是很常见的一个-可能是最常见的一种.但它不是那里唯一的僵局.

这是一个更难解决的死锁示例,如果你 async代码中阻塞(没有上下文)就会出现这个死锁.但是,这种情况更为罕见.