Geo*_*ica 36 c# asp.net-mvc multithreading asynchronous async-await
当我在MVC控制器上进行异步操作时,尤其是在处理I/O操作时,我并不完全理解幕后发生了什么.假设我有一个上传动作:
public async Task<ActionResult> Upload (HttpPostedFileBase file) {
....
await ReadFile(file);
...
}
Run Code Online (Sandbox Code Playgroud)
据我所知,这些是发生的基本步骤:
从线程池中查看新线程并分配给处理命令请求.
当await被命中时,如果调用是I/O操作,则原始线程返回池中,控制转移到所谓的IOCP(输入输出完成端口).我不明白为什么请求仍然存在并等待答案,因为最终调用客户端将等待我们的请求完成.
我的问题是:谁/何时/如何等待完全阻止?
注意:我看到博客文章没有线程,它对GUI应用程序有意义,但对于这个服务器端场景我不明白.真.
Ste*_*ary 23
"网上有一些很好的资源可以详细描述这一点.我写了一篇MSDN文章,高级描述了这一点.
我不明白的是为什么请求仍然存在并等待答案,因为最终调用客户端将等待我们的请求完成.
它仍然存在,因为ASP.NET运行时尚未完成它.完成请求(通过发送响应)是一个明确的行动; 它不像请求会自行完成.当ASP.NET看到控制器操作返回一个Task/时Task<T>,它将不会在该任务完成之前完成请求.
我的问题是:谁/何时/如何等待完全阻止?
什么都没有等待.
可以这样想:ASP.NET有一组正在处理的当前请求.对于给定的请求,一旦完成,就会发送响应,然后从集合中删除该请求.
关键是它是一组请求,而不是线程.这些请求中的每一个在任何时间点都可能有或没有线程处理它.同步请求总是有一个线程(同一个线程).异步请求可能具有没有线程的句点.
注意:我看到了这个帖子:http://blog.stephencleary.com/2013/11/there-is-no-thread.html它对GUI应用程序有意义但是对于这个服务器端场景我不明白.
对于ASP.NET应用程序,I/O的无线方法与GUI应用程序的工作方式完全相同.
最终,文件写入将完成,最终完成从中返回的任务ReadFile.这种"完成任务"的工作通常是通过线程池线程完成的.由于任务现在已完成,Upload操作将继续执行,导致该线程进入请求上下文(即,现在有一个线程再次执行该请求).当Upload方法完成,则任务返回的Upload是完整的,和ASP.NET写出响应,并删除其收藏的要求.
Vil*_*lx- 11
在引擎盖下,编译器会执行一些操作,并通过回调将您的async\ await代码转换为Task基于代码的代码.在最简单的情况下:
public async Task X()
{
A();
await B();
C();
}
Run Code Online (Sandbox Code Playgroud)
变为以下内容:
public Task X()
{
A();
return B().ContinueWith(()=>{ C(); })
}
Run Code Online (Sandbox Code Playgroud)
所以没有魔法 - 只有很多Tasks和回调.对于更复杂的代码,转换也会更复杂,但最终生成的代码在逻辑上等同于您编写的代码.如果你愿意,你可以使用ILSpy/Reflector/JustDecompile中的一个,并亲自查看"引擎盖下"编译的内容.
反过来,ASP.NET MVC基础结构足够智能,可以识别您的操作方法是正常方法还是Task基于方法,并依次改变其行为.因此请求不会"消失".
一个常见的误解是,所有东西都async产生了另一个线程.事实上,它大多相反.在async Task方法的长链的末尾,通常存在执行一些异步IO操作的方法(例如从磁盘读取或通过网络进行通信),这是由Windows本身执行的神奇事物.在此操作期间,根本没有与代码关联的线程 - 它实际上已停止.但是,操作完成后,Windows将回调,然后分配来自线程池的线程以继续执行.有一些框架代码可以保留HttpContext请求,但这就是全部.
usr*_*usr 10
ASP.NET运行时了解任务是什么,并延迟发送HTTP响应,直到任务完成.实际上,Task.Result需要该值才能生成响应.
运行时基本上是这样的:
var t = Upload(...);
t.ContinueWith(_ => SendResponse(t));
Run Code Online (Sandbox Code Playgroud)
因此,当你await遇到你的代码和运行时代码从堆栈中获取并且那时"没有线程"时.该ContinueWith回调复苏的请求,并发送该响应.
| 归档时间: |
|
| 查看次数: |
20189 次 |
| 最近记录: |