NoSQLASP.NET 上之 Async/Await 简介

初稿链接

多数有关 async/await
的在线资源要你在开发客户端应用程序,但在服务器上发出 async
的岗位吗?可以挺自然地对“有”。本文是对准 ASP.NET
上异步请求的概念性概述,并提供了针对超级在线资源的援。我未打算介绍 async
或 await 的语法;因为自都在同篇介绍性的博客文章
( bit.ly/19IkogW) 以及同样首关于 async
最佳做法的文章
( msdn.microsoft.com/magazine/jj991977)
中介绍过了。本文将特别要介绍 async 在 ASP.NET 上之干活规律。

于客户端应用程序,如 Windows 应用商店、Windows 桌面和 Windows Phone
应用程序,async 的重大优点是不错的响应能力。这些项目的应用程序使用 async
主要是为保证用户界面的应能力。对于服务器应用程序,async
异步的要紧优点是正确的可扩展性。Node.js
可扩展性的第一是那个故的异步本质;Open Web Interface for .NET (OWIN)
针对异步进行了崭新设计;ASP.NET 也可以是异步的。Async:不仅仅适用于 UI
应用程序!

一齐跟异步请求处理

于深入探讨异步请求处理程序之前,我想大概地想起同步请求处理程序在 ASP.NET
上的行事原理。在本例中,假而系统受到的请依赖让片表资源,如数据库或
Web API。当接过请求时,ASP.NET
将中间的一个线程池线程分配给该要。因为她是共同编写,所以要处理程序将一同调用该表资源。这将堵住请求线程,直到回到对表资源的调用。
1
 说明了有两个线程的线程池,其中有一个线程被挡住,正在等待外部资源。

NoSQL 1.png)
图 1 一起等待外部资源

末,返回该表资源的调用,并且要线程恢复处理该要。当成功该要,且准备好发送响应时,请求线程将赶回到线程池中。

当下所有好却好,但是若的 ASP.NET
服务器获得的请总会超出其的线程能够处理的数目。这时候,额外的伸手必须顶交有线程可用时才会运转。
2
 说明的依是欠对线程服务器,此时她接受三独请求。

NoSQL 2.png)
祈求 2 收到三独请求的双料线程服务器

于这种状态下,前片只请求都吃分配至线程池中的线程。每个请求都调用外部资源,于是挡了她的线程。第三个请求必须等待有线程可用时,才足以起展开拍卖,但该要都当网中。它的计时器一直于劳作,它刚处在发生
HTTP 错误 503(服务不可用)的惊险之中。

只是对斯考虑一下:这第三单请求在等可用线程,与此同时系统受到的另外两只线程实际上什么都没开。这些线程受到阻碍,都当待返回外部调用。它们确实无开另外实际工作;它们不处在运行状态,也无占用其他
CPU
时间。这些线程被白白浪费掉,但还有请求处于需要中。以下是异步请求解决之情状。

异步请求处理程序的操作办法同是不同。当收到请求时,ASP.NET
将其中的一个线程池线程分配于该要。这无异糟糕,请求处理程序将异步调用该表资源。当回对外表资源的调用之前,已以这个呼吁线程返回到线程池中。
3
 说明当请求于异步等待外部资源时持有两单线程的线程池。

NoSQL 3.png)
贪图 3 异步等待外部资源

首要之界别在于,在进行异步调用的进程被,已以呼吁线程返回到线程池中。当线程处于线程池中经常,它不再和该要相关联。此时,当回外部资源调用时,ASP.NET
将那个线程池中之一个线程重新分配给该要。该线程将持续处理该要。当呼吁完成时,该线程再次归来到线程池中。注意,对于联合处理程序,同一线程会用于该要的凡事生命周期;相反,对于异步处理程序,可以用不同的线程分配受同样请求(在不同之时日)。

本,如果三个请求都进,服务器就可轻松处理了。因为在要于等异步工作时,线程就会吃释放到线程池中,它们可随意处理新的同现有的恳求。异步请求可以被多少较少的线程去处理多少较多之乞求。因此,ASP.NET
上之异步代码的显要优点是出色的但扩展性。

为什么非增加线程池的尺寸?

此时,总是会受咨询到:为什么非增加线程池的深浅?答案来少数单:与阻碍线程池线程相比,异步代码扩展得再深层且还快。

异步代码的扩展性超过拦截线程,这是因其使的内存更不见;在当代操作系统及每个线程池线程具有
1MB
的仓库,外加一个休分页的基础堆栈。这听起来好像多,但当您的服务器上产生同颇堆线程时,会发现实际不敷用。与此相反,异步操作的内存开销要稍得差不多。因此,使用异步操作的乞求于采用阻止线程的呼吁面临重不见的内存压力。异步代码使您得以更多之内存用于其他任务(例如缓存)。

异步代码在速度达到比较阻止线程更快,因为线程池的流入速度有限。截至发稿时,该速度为各国半秒钟一个线程。注入速度有限是件善事;它避免了连的线程构建与损坏。然而,考虑一下请求蜂拥而至时见面产生啊。同步代码很易就会见沦为瘫痪,因为要将故光具有可用之线程,其余请求必须等待线程池有新的线程注入。而单方面,异步代码不需出这般的限量;它是“始终开放”的,可以如此说。异步代码能够重新完美地应请求量突然波动。

告牢记,异步代码不会见取代线程池。不应该单独有线程池或异步代码;而是一旦而拥有线程池和异步代码。异步代码可以让您的应用程序充分利用线程池。它采用现有的线程池,并将其提高至
11。

线程执行异步工作怎么?

自一直深受人咨询到这问题。这意味,必须有一对线程阻止对表资源开展 I/O
调用。因此,异步代码释放要线程,但随即不得不坐牺牲系统受另外一个线程为代价吧?没有,一点提到也远非。

假定了解异步请求为什么扩展,我以跟一个异步 I/O
调用的(简化)示例。假设发生一个伸手需要写副到文件被。请求线程调用异步写副措施。WriteAsync
由基类库 (BCL) 实现,并动用该异步 I/O 的好端口。因此,WriteAsync
调用会作为异步文件写副传递至 OS 中。然后,OS
与驱动程序堆栈进行通信,同时传递数据以写副到 I/O 请求数据包 (IRP) 中。

兹,有趣的事体时有发生了:如果设备驱动程序不克立刻处理
IRP,就必异步进行处理。因此,驱动程序告诉磁盘开始写入,并以“挂于”响应返回到
OS 中。OS 将“挂于”响应传递到 BCL,然后 BCL
将一个未完的职责回到到要处理代码。请求处理代码等以非整的天职由该方式齐处回来的天职。最后,请求处理代码最终向
ASP.NET 返回一个非完全的任务,并且呼吁线程被放出回线程池中。

本,考虑系统的即状态。已经分配了各种 I/O 结构(例如,任务实例和
IRP),而且它们都处于挂起/不完整的状态。然而,没有其余线程因等写入操作完成而受掣肘。ASP.NET、BCL、OS
以及配备驱动程序都不曾特意用于异步工作之线程。

当磁盘完成写副数据经常,它经过暂停通知该驱动程序。该驱动程序会打招呼 OS 该
IRP 已经就,并且 OS 会通过完成端口通知 BCL。线程池线程通过成就于
WriteAsync
返回的职责来响应该通知;这倒过来又回升异步请求代码。在拖欠到位通知等负,短期“借用”了有些线程,但实际上没有线程在写入过程中挨阻碍。

本示例通过大地简化,但是要突出:真正的异步工作并不需要线程。实际推送字节也不论需占用
CPU
时间。还有一个扶助课程要了解。考虑一下在设施驱动程序的社会风气里,设备驱动程序如何做才会即刻或异步处理
IRP。同步处理是不是一个拣。在设施驱动程序级别,所有主要之 I/O
都是异步的。许多开发人员的思索模式都是把用于 I/O 操作的“普通
API”认为是同的,异步 API 作为一如既往叠建立于日常的联手 API
上。然而,这恰恰相反:实际上,普通 API是​​异步的;使用异步 I/O
实现的凡多亏同 API!

胡没有了异步处理程序?

一旦异步请求处理是这般完美,那它为什么还不可用?事实上,异步代码非常适合扩展,因此于
Microsoft .NET Framework 形成的新到现在,ASP.NET
平台一直支撑异步处理程序和模块。ASP.NET 2.0 引入了异步网页,ASP.NET MVC
2 中 MVC 得到了异步控制器。

唯独,最近,异步代码在编排上连接有点题目,并且难于保护。许多柜就决定联合开发代码、支付更怪之劳动器场或更高昂之托管,这样即便会简单有。现在,出现了逆转:在
ASP.NET 4.5 中,使用 async 和 await
的异步代码几乎和编辑并代码一样简单。由于大型系统迁移至云托管并求更有规模,越来越多之店铺开侧重
ASP.NET 上的 async 和 await。

异步代码不是灵丹妙药

异步请求处理尽管十分强大,但其不会见迎刃而解所有题目。关于 ASP.NET 上之 async 和
await 可以举行啊的题目,存在一些宽广的误会。

当有的开发人员了解 async 和 await
后,他们当当下是服务器代码“让步”于客户端(例如浏览器)的一模一样种植办法。然而,ASP.NET
上的 async 和 await 只“让步”于 ASP.NET 运行时;HTTP
协议保无变换,您对每个请求按照只有出一个应。如果当 async/await 之前若需
SignalR 或 AJAX 或 UpdatePanel,那么在 async/await 之后仍要 SignalR
或 AJAX 或 UpdatePanel。

采取 async 和 await
的异步请求处理好帮忙扩大而的应用程序规模。然而,这是于平等雅服务器上的扩张;您可能依然要对扩大进行设计。如果您确实需要扩大体系布局,将仍旧要考虑无状态的幂等请求和可靠的阵。Async/await
多少有帮助:它们一旦您能够充分利用服务器资源,所以您不需要常开展扩张。但是,如果你确实需要往外扩张,您将用一个适当的分布式体系布局。

ASP.NET 上之 async 和 await 都是有关 I/O
的。它们非常适合读取和描写副文件、数据库记录以及 REST
API。然而,它们不可知生好地推行占用大量 CPU 的天职。您得经等待
Task.Run 开始部分背景工作,但这么做没有其它意义。事实上,通过启发式干扰
ASP.NET 线程池会损害而的但是扩展性。如果您要于 ASP.NET 上执行占用大量 CPU
的做事,最好的措施是直在伸手线程上推行该工作。通常,不要将工作排队送至
ASP.NET 上之线程池。

终极,在整上考虑系统的而是扩展性。十年前,常见的系布局使发生一个而与后端的
SQL Server 数据库进行通信的 ASP.NET Web
服务器。在这种简易的系布局面临,通常数据库服务器是只是扩展性的瓶颈,而非是
Web
服务器。让您的数据库调用异步可能打未至赞助作用;当然你可用它来扩大
Web 服务器,但数据库服务器将挡整个体系的恢宏。

Rick Anderson
于外优秀的博客文章被对异步数据库调用给有案例,“我的数据库调用应该是异步的也罢?”( bit.ly/1rw66UB)。以下是有限触及支撑论据:首先,异步代码有难度(因而开发人员的日成本比只是购买较生之服务器如果大);其次,如果数据库后端是瓶颈,那么扩展
Web
服务器无啊意思。在形容这首文章时,这点儿上面的论据充分有道理,但随着岁月之推移这有限单论据的义早已日趋减弱。首先,使用
async 和 await
编写异步代码更加爱了。其次,随着全球逐步以言语计算,网站的数目后端逐渐获得扩展。诸如
Microsoft Azure SQL 数据库、NoSQL 以及其他 API 之类的当代后端与单个 SQL
Server 相比可以博重新进一步的恢弘,从而以瓶颈又推动回 Web
服务器。在这种状况下,async/await 可以经扩张 ASP.NET 带来巨大的优势。

每当始发前

先是你得知道只有 ASP.NET 4.5 支持 async 和 await。有一个称呼
Microsoft.Bcl.Async 的 NuGet 程序包可也 .NET Framework 4 启用 async 和
await,但连无应用她;这将无法正常办事!其缘由是,为了能够及 async 和 await
更好地同步坐班,ASP.NET 本身必须重新改其管理异步请求处理的不二法门;NuGet
程序包被包含编译器需要的有着品类,但非见面补 ASP.NET
运行时。没有缓解措施;您要 ASP.NET 4.5 或再次强版本。

对接下去,要解,ASP.NET 4.5 在服务器上引入了“quirks
模式”。如果您创建一个新的 ASP.NET 4.5
项目,则不用担心。但是,如果要是用现有的品类升级到 ASP.NET 4.5,所有 quirk
都将于辟。我建议乃​​通过编制 web.config 并以
httpRuntime.targetFramework 设置为 4.5
把它们整个停歇。如果采取这个设置的应用程序失败(并且您不思量花时错开修复它),至少你可以经过也
aspnet:UseTaskFriendlySynchronizationContext 的 appSetting
键添加值“true”来获得 async/await 工作。如果您将
httpRuntime.targetFramework 设置为 4.5,则 appSetting 键不必要。Web
开发组织曾经当bit.ly/1pbmnzK 发表一首有关这无异于初的“quirks
模式”的详细信息的博客。提示:
如果你看看出现意想不到之一言一行要例外情况,并且您的调用堆栈包括
LegacyAspNetSynchronizationContext,那么您的应用程序正于斯“quirks
模式”下运作。LegacyAspNetSynchronizationContext 与异步不般配;您在
ASP.NET 4.5 上待正常的 AspNetSynchronizationContext。

当 ASP.NET 4.5 中,所有的 ASP.NET
设置都对准异步请求设置了颇好之默认值,但为时有发生几乎只其他装置您或许使反。首先是
IIS 设置:考虑以 IIS/HTTP.sys
的队限制(应用程序池|高级设置|队列长)从默认的 1,000 提高至
5,000。另一个凡 .NET
运行时设置:ServicePointManager.DefaultConnectionLimit,它的默认值是根本数量之
12 加倍。DefaultConnectionLimit 限制到同一主机名之不胫而走连接数。

有关中止请求的提醒

当 ASP.NET
同步处理一个央时,它发出一个非常简单的编制可以中止请求(例如,如果要过其超时值):它会搁浅该要的劳作线程。这是发道理的,因为在共同领域,每个请求于开头交为止还利用以及一个办事线程。中止线程对于
AppDomain 的一劳永逸平稳而言尚非周全,因此默认情况下 ASP.NET
将为期回收而的应用程序,以保干净。

对异步请求,如果如中断请求,ASP.NET
并无见面停顿工作线程。相反,它会吊销以 CancellationToken
的要。异步请求处理程序应该受并遵守取消标记。大多数比较新的框架(包括
Web API、MVC 与 SignalR)将构建并直通往你传递
CancellationToken;您需开的饶是管其声明也一个参数。您吗堪直接访问
ASP.NET 标记;例如,HttpRequest.TimedOutToken 是当呼吁过时时取消的一个
CancellationToken。

随着应用程序迁移至讲话,中止请求虽展示更为重要。基于云的应用程序也尤为依赖让或占任意时间量的标服务。例如,一种标准模式是动指数回退来重试外部请求;如果您的应用程序依赖让类似这样的强劳动,对而的恳求处理在完整达标采取一个过期上限不失为一个好法子。

Async 支持的现状

本着 async 的兼容性问题,已针对成千上万库房进行了创新。在本 6 中就以 async
支持添加到实体框架(在 EntityFramework NuGet
程序包中)。不过,当因为异步方式运行时,您要要小心操作以避免延迟加载,因为延迟加载总是坐协同方式实施。HttpClient(在
Microsoft.Net.Http NuGet 程序包中)是采取 async 理念设计要成的现世 HTTP
客户端,是调用外部 REST API 的精选择;是 HttpWebRequest 和 WebClient
的现世版替代品。在 2.1 版本中,Microsoft Azure 存储客户端库(在
WindowsAzure.Storage NuGet 程序包中)添加了异步支持。

比较新的框架(如 Web API 和 SignalR)对 async 和 await
提供完善的支持。个别 Web API 已围 async
支持成立由任何管道:不仅产生异步控制器,还有异步筛选器和处理程序。Web API
和 SignalR 有一个要命平凡的异步故事:您得“放手去做”然后“就会成功”。

及时给我们带了一个使人难过的故事:如今,ASP.NET MVC 只是一些支持 async
和 await。有核心的支撑——异步控制器的操作与注销工作正常化。ASP.NET
网站及有关于如何用 ASP.NET MVC 中的异步控制器操作的优教程
( bit.ly/1m1LXTx);这对 MVC 上的 async
入门是绝佳的资源。不幸的凡,ASP.NET MVC (目前)不支持异步筛选器
( bit.ly/1oAyHLc) 和异步子操作
( bit.ly/1px47RG)。

ASP.NET Web 窗体是一个比旧的框架,但她为尽管支持 async 和
await。并且,ASP.NET 网站上关于异步 Web 窗体的课也是入门的绝佳资源
( bit.ly/Ydho7W)。有矣 Web
窗体,异步支持好选取进入。您要先以 Page.Async 设置为
true,然后您可以下 PageAsyncTask
通过该页面注册异步工作(或者,您可以使用 async void
事件处理程序)。PageAsyncTask 也支持取消。

若你有一个自定义 HTTP 处理程序或 HTTP 模块,那么 ASP.NET
现在呢得以支撑其的异步版本。HTTP 处理程序是经过 HttpTaskAsyncHandler
( bit.ly/1nWpWFj) 进行支持之,HTTP 模块是透过
EventHandlerTaskAsyncHelper ( bit.ly/1m1Sn4O)
进行支撑之。

以至于发稿时,ASP.NET 团队在开名也 ASP.NET vNext 的一个新路。在
vNext 中,默认情况下整个管道是异步的。目前,该计划以 MVC 和 Web API
合并及能到支持
async/await(包括异步筛选器和异步视图组件)的十足框架中。其他异步就绪框架(如
SignalR)会当 vNext 中找到一个本来的小。当然,未来凡是 async 的海内外。

讲究安全网

ASP.NET 4.5
中引入了几只新的“安全网”,帮助你捕捉应用程序中之异步问题。这些是默认情况下在的,应当保留。

当一头处理程序试图实施异步工作时,您的 InvalidOperationException
会收到这么的音讯,“此时匪可知开异步操作”。有少数个重点原因导致出现是很。第一,Web
窗体页有异步事件处理程序,但忽略了将 Page.Async 设置为
true。第二,同步代码调用 async void 方法。这是吗是免 async void
的其它一个缘故。

旁一个安全网适用于异步处理程序:当异步处理程序完成请求,但 ASP.NET
检测及异步工作无完成时,您的 InvalidOperationException
会收到这么的信,“异步模块或处理程序已做到,但异步操作还是居于挂于状态”。这便是由于异步代码调用
async void 方法而导致的,但也或是坐不当使用基于事件之异步模式 (EAP)
组件 ( bit.ly/19VdUWu)。

您还足以采取一个选项来关闭这简单单安全网:HttpContext.AllowAsyncDuringSyncStages(也得以于
web.config 中针对其进行设置)。Internet
上之片段页面建议您当观望这些杀时进行这样的安装。我完全无允许。说实在,我未亮堂这怎么行。禁用安全网是一个骇人听闻的想法。我能够想到的绝无仅有可能的原委是,您的代码可能已经展开了有些良先进的异步处理(远超我就尝试过的限),您是一个大抵线程处理的天分。所以,如果您就读了整篇文章,边打在呵欠边想,“拜托,我不过免是菜鸟”,那么自己眷恋你可考虑禁用安全网。而对于我们中之其他人,这是一个不胜危险的挑选项,除非您了知晓后果,否则不应开展这设置。

开班采用

算到结尾了!准备好起来以 async 和 await 了吧?我异常欣赏你的耐心。

首先,查看本文的“异步代码不是灵丹妙药”一省以担保 async/await
对你的系统布局是有利于之。接下来,将公的应用程序更新至 ASP.NET 4.5 并关闭
quirks
模式(此时如一味吧保证无产生中断,运行它们为得以)。这时,您便可初步真的的共同/等待工作了。

起“叶”开始。想想你的恳求什么进行处理并标识外依据 I/O
的操作,特别是基于网络的操作。常见的言传身教是数据库查询以及指令,以及针对其他
Web 服务同 API 的调用。选择一个来开,并做有检察来查找使用
async/await 执行该操作的顶尖选项。.NET Framework 4.5 中发生许多搭 BCL
类型目前还早已异步就绪;例如,SmtpClient 具有 SendMailAsync
方法。某些项目可以提供异步就绪更换;例如,HttpWebRequest 和 Web
客户端好据此 HttpClient 来转换。如得,请升级而的库版本;例如,EF6
中的实体框架具有异步兼容方法。

而是,要避库中之“假异步”。假异步是这般平等种植状况:一个组件中兼有一个异步就绪
API,而其独自是经过将合 API 封装在线程池线程内来贯彻的。这对实现
ASP.NET 上的可是扩展性适得其反。假异步的一个榜首示范就是 Newtonsoft
JSON.NET,一个另地方十分不错的库房。最好不调用(假)异步版本来实行 JSON
的序列化;只待换做调用同步版本即可。假异步的一个寸步难行示例就是 BCL
文件流。当打开一个文书流时,必须坐显式方式打开以便为异步访问;否则,它会采取假异步,同步阻止文件读取和写入操作着之线程池线程。

选一个“叶”之后,就足以起来以代码中调用该 API
的艺术,使的成为通过等待调用异步就绪 API 的异步方法。如果您调用的 API
支持 CancellationToken,您的办法应该采取 CancellationToken 并以那个传递让
API 方法。

万一用一个措施标记为异步,就应有改成其回到路:void 变为“Task”,非 void
类型 T
变为“Task<T>”。您见面意识,所有拖欠方法的调用者都急需变成异步,以使她会等任务,等等。此外,将
Async 附加到公方法的名号被,以本基于任务的异步模式约定
( bit.ly/1uBKGKR)。

同意 async/await 模式将公的调用堆栈向“Trunk”进行扩展。在 Trunk
中,您的代码用与 ASP.NET 框架(MVC、Web 窗体,Web
API)相连接。阅读本文前面所陈述之“异步支持的现状”一省中之系学科,将你的异步代码和框架进行合并。

附带搜有其他地方线程的状态。由于异步请求或会见转线程,所以地方线程状态(如
ThreadStaticAttribute、ThreadLocal<T>、线程数据插槽和
CallContext.GetData/SetData)将未可用。如果可能的话,请以
HttpContext.Items 进行转换;或者你可拿不可变的数据存储于
CallContext.LogicalGetData/LogicalSetData 中。

以下是本人发觉的管事技巧:您得(暂时)复制您的代码来创造一个笔直分区。利用这种技能,您不要拿旅方法更改为异步;可以复制整个合方法,然后以副本改呢异步。然后,您得于大部分应用程序保持以并方法,只开创异步的一个不怎么垂直片即可。如果您想管异步作为概念证明来进行探索还是独自针对应用程序的一律有些实行负载测试来体验如何扩展您的网,这会是一个很过硬的呼声。您得享有一个截然异步的请(或页面),而应用程序的其余部分保持为一起。当然,您不指望对每一个方都封存副本;最终,所有的
I/O 绑定代码都拿凡异步的,可以去并副本。

总结

自身愿意本文能够协助而了解 ASP.NET 上之异步请求的底子概念。使用 async 和
await,可以假设编写能够尽特别限度地使用该服务器资源的 Web 应用程序、服务与
API 变得比较往常别时刻还再次易于。Async 真是极强了!

 

网站地图xml地图