教程有时会指向自己的异步方法的实现,例如以下代码:
async public static Task GetHttpResponseAsync()
{
using (HttpClient httpClient = new HttpClient())
{
HttpResponseMessage response = await httpClient.GetAsync(...);
Console.WriteLine(response.Something);
}
}
Run Code Online (Sandbox Code Playgroud)
我很清楚异步一般是如何工作的,但没有一个教程解释内部是如何实现的
httpClient.GetAsync(...);
Run Code Online (Sandbox Code Playgroud)
这对于详细了解异步代码的工作原理非常重要。让我好奇的是 GetAsync 的内部操作(那些方法或其他异步方法)是否在执行此代码的某种容器中注册?操作系统是否必须支持异步方法(fe 它使用 windows api)?如果我想实现我自己的异步文件下载器(从磁盘并且没有来自 .NET 框架的重要部分),我将如何实现它,我应该在某处注册我的方法以供进一步调用吗?
对我来说很清楚,在内部,编译器制作状态机,在 DoSomething() 方法完成它必须做的事情之后,它只是再次调用这个状态机以在等待后继续执行代码。
另外我不清楚的是异步代码如何在同一线程上运行。我认为维护状态机必须在同一个线程上,但是 httpClient.GetAsync() 中的代码如何可以在同一个线程上运行并且不中断其他操作(fe gui)。必须有一些东西使此代码在单独的线程上运行(在所有情况下)。我错了吗?我错过了什么?
我的问题的附加解释:在 JavaScript 中,据我所知和理解,异步方法的工作原理是将它们注册到某种容器中(在单独的线程上一个一个地运行它们),该容器执行此方法。方法执行完成后,结果返回到用户上下文,我很清楚,这里的工作方式是否相同?
简而言之,必须在操作系统级别提供真正的异步。正如您所指出的,async
/await
是一种语言级功能,它使用编译器生成的状态机将您的方法“分解”成可以异步运行的部分,但它依赖于操作系统原语(中断、线程)来实际执行这项工作异步方式。
但是,需要注意的是,通常不会创建线程来处理异步操作。我将遵循这篇专业撰写的文章来描述为什么会这样:http : //blog.stephencleary.com/2013/11/there-is-no-thread.html
在 Windows 上,执行异步工作的主要机制是使用I/O 完成端口。这是许多 .NET 类型(包括HttpClient
您正在使用的)在幕后使用的 Windows API 。
请注意,对于非 I/O 操作,您也可以始终使用Threads或更好的Thread Pool API来执行将异步完成的后台工作。
Windows API 中的ReadFile(或更新的ReadFileEx)函数旨在与异步 I/O 一起使用。当您调用 时ReadFile
,您可以使用该FILE_FLAG_OVERLAPPED
标志并将一个OVERLAPPED
结构传递给该lpOverlapped
参数,从而启用异步读取。我鼓励你使用这个 API 来设计你的文件下载器。
总之:
HttpMessageHandler.cs
-> HttpClientHandler.Windows.cs
-> WinHttpHandler.cs
-> Interop.winhttp.cs
-> PInvoke 进入WinHTTP API本机 DLL -> 带有 I/O 完成端口的 Winsock 套接字。