Async-Await控制台程序未达预期:为何异步任务提前完成?
问题描述
我编写了一段简单的async-await代码,当FetchAndShowHeaders方法中的await被触发时,后续逻辑是Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(response.Headers.ToString()));。
我不理解的点在于:顶层main方法尚未执行到await q时,FetchAndShowHeaders方法为何已经完成?我原本预期程序会先打印完所有的点,再输出请求结果,但实际却是在打印点的过程中,该方法就完成了输出。这是为什么?
代码示例
Console.WriteLine("soner1"); Task q = FetchAndShowHeaders("http://www.fultonbank.com"); Console.WriteLine("soner2"); for (int i = 0; i < 500_000_0; i++) { for (int ii = 0; ii < 500; ii++) { if (i % 500_000 == 0) Console.Write("."); } } Console.WriteLine("\nsoner3"); await q; Console.WriteLine("soner4"); return; async Task FetchAndShowHeaders(string url) { using var w = new HttpClient(); var req = new HttpRequestMessage(HttpMethod.Head, url); var response = await w.SendAsync(req, HttpCompletionOption.ResponseHeadersRead); Console.WriteLine(System.Text.Json.JsonSerializer.Serialize( response.Headers.ToString())); }
实际输出
/Users/soner/RiderProjects/yea/yea/bin/Debug/net6.0/yea soner1 soner2 ...................................................................................................... ........................................................................... ......................................................................................................... .................................................."Date: Mon, 28 Aug 2023 12:24:07 GMT\nConnection: keep-alive\nSet-Cookie: AWSB9/69VjCnga384ol7q; Expires=Mon, 04 Sep 2023 12:24:06 GMT; Path=/; SameSite=None; Secure, ASP.NET_SessionId=i2hpoollqqqqud1a pz2gqac; path=/; HttpOnly; SameSite=Lax, ASP.NET_SessionId=i2hqqqd1apz2gqac; path=/; HttpOnly; SameSite=Lax, Fulton.Foundation.ContactIdent ification.Cookies.ContactIdentificationCookieManagerblablalblalbla" ............................ .......................... ......................................................................................................................... .......................... .......................... ......................................................................... ................................................................. soner3 soner4 Process finished with exit code 0.
原因解释
这是异步编程的核心特性——非阻塞执行导致的,具体拆解如下:
任务启动即并发
当执行Task q = FetchAndShowHeaders("http://www.fultonbank.com");时,FetchAndShowHeaders会立即启动执行:先创建HttpClient和请求消息,随后调用SendAsync发起HTTP请求。这一步是异步非阻塞的:请求被发送后,当前线程不会原地等待服务器响应,而是直接返回,继续执行main方法里的后续代码(打印soner2、进入循环打印点)。await是"暂停并让出线程",而非"等待到调用await时才继续"
FetchAndShowHeaders里的await w.SendAsync(...)会让方法暂停执行,把线程交还给线程池,此时方法并未完成,只是处于等待状态。当服务器返回响应头后,线程池会分配线程继续执行await之后的代码(序列化并打印响应头)。这个过程完全独立于main的循环,所以会和打印点的操作并发执行。await q仅保证后续代码等待任务完成,不影响任务前期执行
main里的await q只是确保soner4的打印必须在q任务完成后才进行,但它不会阻止q任务在这之前完成。只要服务器响应返回,q任务就会立刻执行后续代码并输出结果,不需要等到await q被调用。
内容的提问来源于stack exchange,提问作者Soner from The Ottoman Empire

