cat*_*yes 8 c# deadlock async-await asp.net-core
我读过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 };
}
}
Run Code Online (Sandbox Code Playgroud)
我预计这段代码会死锁,因为一旦await Task.Delay(3000);完成,DoSomethingAsync()需要输入被阻止的请求上下文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
Run Code Online (Sandbox Code Playgroud)
我还创建了一个普通的.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";
}
}
Run Code Online (Sandbox Code Playgroud)
我认为这也应该死锁,因为var result = asyncTest.DoSomethingAsync().Result;阻塞主线程,并DoSomethingAsync()捕获主线程的上下文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();服务于其余代码的线程池中有一个新线程之后.那么,如何将其恢复name,age,然后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}" };
}
Run Code Online (Sandbox Code Playgroud)
因为ASP.NET Core没有同步上下文。
更新资料
如果您想了解Jon Skeet的幕后工作原理,请阅读它的精彩博客文章:https : //codeblog.jonskeet.uk/category/eduasync/
| 归档时间: |
|
| 查看次数: |
1555 次 |
| 最近记录: |