繁体   English   中英

为什么ASP.NET Core Web API中没有这种死锁?

[英]Why doesn't this deadlock in ASP.NET Core Web API?

我读过Stephen Cleary的帖子Do not Block on Async Code ,所以我创建了一个ASP.NET Core Web API项目:

class AsyncTest
{
    public async Task<string> DoSomethingAsync()
    {
        await Task.Delay(3000);
        return "I'm back";
    }
}

[Route("api/[controller]")]
public class ValuesController : Controller
{
    // GET api/values
    [HttpGet]
    public IEnumerable<string> Get()
    {
        var asyncTest = new AsyncTest();
        var result = asyncTest.DoSomethingAsync().Result;
        return new string[] { result };
    }
}

我预计这段代码会陷入僵局,因为曾经await Task.Delay(3000); 完成后, DoSomethingAsync()需要输入被var result = asyncTest.DoSomethingAsync().Result;阻止的请求上下文var result = asyncTest.DoSomethingAsync().Result; 但它没有死锁并且没有问题就返回! ASP.NET Core Web API的行为是否不同?

我正在使用dotnet --info

.NET Command Line Tools (2.1.2)

Product Information:
 Version:            2.1.2
 Commit SHA-1 hash:  5695315371

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.14393
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\2.1.2\

Microsoft .NET Core Shared Framework Host

  Version  : 2.0.3
  Build    : a9190d4a75f4a982ae4b4fa8d1a24526566c69df

我还创建了一个普通的.NET Framework 4.6.1 控制台应用程序:

class Program
{
    static void Main(string[] args)
    {
        var asyncTest = new AsyncTest();

        var result = asyncTest.DoSomethingAsync().Result;
    }
}

class AsyncTest
{
    public async Task<string> DoSomethingAsync()
    {
        await Task.Delay(3000);
        return "I'm back";
    }
}

我认为这也应该是死锁,因为var result = asyncTest.DoSomethingAsync().Result; 阻塞主线程, DoSomethingAsync()await Task.Delay(3000);上捕获主线程的上下文await Task.Delay(3000); await Task.Delay(3000); 完成后,为了恢复, DoSomethingAsync()需要输入被阻塞的主线程的上下文。 但它实际上也没有死锁。 我错在哪里?

更新

正如其他人所指出的, ASP.NET Core控制台应用程序中没有同步上下文 ,现在我的问题是,在传统的ASP.NET应用程序中,同步上下文负责在继续时恢复HttpContext.Current之类的事情。等待的异步操作完成并且exectution重新开始,那么一旦ASP.NET核心应用程序在新挑选的线程池线程上恢复后,它如何使这些东西像HttpContext.Current一样可用?

此外,对于任何没有自己的精巧线程的操作,假设它在线程池线程(如ASP.NET应用程序ASP.NET核心应用程序等)上运行,并等待另一个异步操作。 当它开始等待时,它会产生它当前正在运行的线程。 但是,一旦等待的异步操作完成,它将在另一个线程池线程上恢复,如何将先前的线程上下文(如局部变量等)恢复到新选择的线程上? 例如,在下面的代码中,在var result = await DoSomethingAsync(); 服务于其余代码的线程池中有一个新线程。 那么它如何恢复nameage ,然后return new string[] { $"{name} at {age}: {result}" }; 可以用吗? 局部变量存储在属于特定线程的线程调用堆栈中。 所以我认为必须有继续的线程上下文切换 await调用之前,需要存储当前线程池线程的线程上下文,在await调用之后,需要将存储的线程上下文恢复到新挑选的线程上下文。 如果是这样,成本将不便宜。 我的理解在这里是否正确?

[HttpGet]
public async Task<IEnumerable<string>> GetAsync()
{
    var name = "John Doe";
    var age = 20;
    var result = await DoSomethingAsync();
    return new string[] { $"{name} at {age}: {result}" };
}

因为ASP.NET Core没有同步上下文。

更新

Jon Skeet有一个很棒的博客系列,如果你想了解它的工作原理: https//codeblog.jonskeet.uk/category/eduasync/

对于ASP.NET Core, 没有SynchronizationContext ,这意味着在ASP.NET Core中不会发生指定的阻塞情况。 控制台应用也是如此。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM