在ASP.NET的上下文中,为什么在调用异步方法时Task.Run(...).Result不死锁? [英] In the context of ASP.NET, why doesn't Task.Run(...).Result deadlock when calling an async method?

查看:305
本文介绍了在ASP.NET的上下文中,为什么在调用异步方法时Task.Run(...).Result不死锁?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I created a simple WebApi project with a single controller and a single method:

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;
    }
}

我对异步/等待和任务有很好的理解;几乎是在宗教上追随史蒂芬·克莱里(Stephen Cleary).只是.Result的存在使我感到焦虑,我希望这会陷入僵局.我知道Task.Run(...)是浪费的,导致在等待异步DoIt()完成时占用线程.

I have a good understanding of async/await and tasks; almost religiously following Stephen Cleary. Just the existence of .Result makes me anxious, and I expect this to deadlock. I understand that the Task.Run(...) is wasteful, causing a thread to be occupied while waiting for the async DoIt() to finish.

问题是这不是僵局,它使我感到心.

The problem is this is not deadlocking, and it's giving me heart palpitations.

我看到了一些答案,例如 https://stackoverflow.com/a/32607091/1801382 ,还观察到在执行lambda时SynchronizationContext.Current为null.但是,还有一些类似的问题要问为什么上面的代码死锁,而我已经看到在将ConfigureAwait(false)与(.Result)结合使用(未捕获上下文)的情况下会发生死锁

I see some answers like https://stackoverflow.com/a/32607091/1801382, and I've also observed that SynchronizationContext.Current is null when the lambda is executing. However, there are similar questions to mine asking why the above code does deadlock, and I've seen deadlocks occur in cases where ConfigureAwait(false) is used (not capturing the context) in conjunction with .Result.

有什么作用?

推荐答案

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

Result by itself isn't going to cause a deadlock. A deadlock is when two parts of the code are both waiting for each other. A Result is just one wait, so it can be part of a deadlock, but it doesn't necessarily always cause a deadlock.

标准示例中, Result等待任务完成,同时保持上下文,但是该任务无法完成,因为它正在等待上下文免费.所以有一个僵局-他们在互相等待.

In the standard example, the Result waits for the task to complete while holding onto the context, but the task can't complete because it's waiting for the context to be free. So there's a deadlock - they're waiting for each other.

ASP.NET Core根本没有上下文,因此Result不会在那里僵持. (出于其他原因,这仍然不是一个好主意,但不会出现死锁).

ASP.NET Core does not have a context at all, so Result won't deadlock there. (It's still not a good idea for other reasons, but it won't deadlock).

问题在于这不是僵局,而是让我感到心pal.

The problem is this is not deadlocking, and it's giving me heart palpitations.

这是因为Task.Run任务不需要上下文即可完成.因此,调用Task.Run(...).Result是安全的. Result正在等待任务完成,并且保持上下文(防止在该上下文中执行请求的任何其他部分),但这没关系,因为Task.Run任务不需要位于以下位置的上下文全部.

This is because the Task.Run task does not require the context to complete. So calling Task.Run(...).Result is safe. The Result is waiting for the task to complete, and it is holding onto the context (preventing any other parts of the request from executing in that context), but that's OK because the Task.Run task doesn't need the context at all.

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

Task.Run is still not a good idea in general on ASP.NET (for other reasons), but this is a perfectly valid technique that is useful from time to time. It won't ever deadlock, because the Task.Run task doesn't need the context.

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

However, there are similar questions to mine asking why the above code does deadlock,

相似但不精确.仔细查看这些问题中的每个代码语句,并问问自己它在什么上下文中运行.

Similar but not exact. Take a careful look at each code statement in those questions and ask yourself what context it runs on.

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

and I've seen deadlocks occur in cases where ConfigureAwait(false) is used (not capturing the context) in conjunction with .Result.

Result/上下文死锁是一个非常常见的死锁-可能是最常见的死锁.但这不是唯一的死锁情况.

The Result/context deadlock is a very common one - probably the most common one. But it's not the only deadlock scenario out there.

这是一个例子如果您在 async代码(上下文)中阻塞,则可能会出现更困难的死锁.但是,这种情况很少见.

Here's an example of a much more difficult deadlock that can show up if you block within async code (without a context). This kind of scenario is much more rare, though.

这篇关于在ASP.NET的上下文中,为什么在调用异步方法时Task.Run(...).Result不死锁?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆