我在Jon Skeet的“C#深度。第3版”中找到了以下示例:
static async Task<int> GetPageLengthAsync(string url)
{
using (HttpClient client = new HttpClient())
{
Task<string> fetchTextTask = client.GetStringAsync(url);
int length = (await fetchTextTask).Length;
return length;
}
}
public static void Main()
{
Task<int> lengthTask = GetPageLengthAsync("http://csharpindepth.com");
Console.WriteLine(lengthTask.Result);
}
我希望这段代码应该死锁,但事实并非如此。
在我看来,它的工作原理如下:
Main
方法在主线程内同步调用GetPageLengthAsync
。 GetPageLengthAsync
发出异步请求并立即将Task<int>
返回Main
说“等待一段时间,我会在一秒钟内返回一个int”。 Main
继续执行并偶然发现lengthTask.Result
导致主线程被阻塞并等待lengthTask
完成其工作。GetStringAsync
完成并等待主线程可用于执行Length
并开始继续。但似乎我误解了一些东西 为什么这段代码没有死锁? this Stackoverflow question about await/async deadlock中的代码似乎也是这样,但死锁。
答案 0 :(得分:10)
await
返回原始同步上下文,无论是UI线程(在桌面UI应用程序中)还是ASP.NET(非核心)中的请求上下文。
在GUI应用程序中,由于UI线程被.Result
锁定,因此您将遇到死锁。 await
将永远等待这个电话结束。
控制台应用程序和ASP.NET Core没有同步上下文,因此调用.Result
不会导致死锁。
PS for VS 15.3:
Visual Studio 2017 15.3 Preview 2(gasp)允许异步主应用程序。有了它,你可以写:
public static Task Main()
{
var length = await GetPageLengthAsync("http://csharpindepth.com");
Console.WriteLine(length);
}