充分发挥异步在 ASP.NET 中的强大优点

做者:Brij Bhushan Mishrahtml

最近几年,异步编程受到极大关注,主要是出于两个关键缘由:首先,它有助于提供更好的用户体验,由于不会阻塞 UI 线程,避免了处理结束前出现 UI 界面挂起。其次,它有助于大幅扩展系统,并且无需添加额外硬件。web

可是,编写合适的异步代码来管理线程自己是项乏味的工做。虽然如此,其巨大好处让许多新旧技术纷纷开始使用异步编程。微软自发布了 .NET 4.0之后也对其投入颇多,随后在 .NET 4.5中引入了 async 和 await 关键字,使异步编程变得史无前例地简单。数据库

可是,ASP.NET 中的异步功能自一开始就可使用,只是历来没有获得应有的重视。并且,考虑到 ASP.NET 和 IIS 处理请求的方式,异步体现的优点可能更明显。经过异步,咱们很容易就能够大幅提升 ASP.NET 应用程序的扩展性。随着新的编程结构引入,如 async 和 await 关键字,咱们也应该学会使用异步编程的强大功能。编程

在本篇博文中,咱们将讨论一下 IIS 和 ASP.NET 处理请求的方式,而后看看 ASP.NET 中哪些地方可使用异步,最后再讨论几个最能体现异步优点的场景。浏览器

##请求是如何处理的?服务器

每一个 ASP.NET 请求都要先经过 IIS,而后再由 ASP.NET 处理程序进行最终处理。 首先IIS 接收请求,初步处理后,发送给ASP.NET(必须是一个ASP.NET请求),而后由ASP.NET进行实际处理并生成响应,以后该响应经过IIS发回给客户。在IIS上,有一些工做进程负责从队列中取出请求,并执行IIS 模块,而后再将该请求发送到ASP.NET 队列。可是,ASP.NET自己不建立任何线程,也没有处理请求的线程池,而是经过使用CLR 线程池,从中获取线程来处理请求。所以,IIS 模块调用ThreadPool.QueueUserWorkItem,将请求排入队列,供CLR 工做线程处理。咱们都知道,CLR线程池是由CLR管理,而且可以自动调整(也就是说,它根据须要建立和销毁进程)。这里还要记住,建立和销毁线程是项很繁重的任务,这就是为何CLR线程池容许使用同一个线程处理多个任务。下面来看一个描述请求处理过程的图示。网络

在上图中能够看到,请求首先由 HTTP.sys接收,并添加到相应内核级应用程序池队列。而后,一个IIS工做线程从队列中取出请求,处理后将其传到ASP.NET 队列。注意,该请求若是不是一个ASP.NET请求,将从 IIS 自动返回。最后,从CLR线程池中分配一个线程,负责处理该请求。异步

##ASP.NET中异步的使用场景是?socket

全部请求大体能够分为两类:async

  1. CPU Bound 类
  2. I/O Bound 类

CPU Bound 类请求,须要 CPU 时间,并且是在同一进程中执行;而 I/O Bound 类请求,自己具备阻塞性,须要依赖其余模块执行 I/O 操做并返回响应。阻塞性请求是提升应用程序可伸缩性的主要障碍,并且大多数web应用程序中,在等待 I/O 操做的过程当中浪费了大量时间。 所以如下场景适合使用异步:

  1. I/O Bound 类请求,包括:

    a. 数据库访问

    b. 读/写文件

    c. Web 服务调用

    d. 访问网络资源

  2. 事件驱动的请求,好比SignalR

  3. 须要从多个数据源获取数据的场景

做为示例,这里建立一个简单的同步页面,而后再将它转换成异步页面。 本示例设置了1000ms的延迟(以模拟一些繁重的数据库或web服务调用等),并且还使用WebClient下载了一个页面,以下所示:

protected void Page_Load(object sender, EventArgs e)

    {

        System.Threading.Thread.Sleep(1000);

        WebClient client = new WebClient();

        string downloadedContent = client.DownloadString("https://msdn.microsoft.com/en-us/library/hh873175%28v=vs.110%29.aspx");

        dvcontainer.InnerHtml = downloadedContent;

    }

如今将该页面转换成异步页面,这里主要涉及三个步骤:

  1. 在页面指令中添加Async = true,将该页面转换成异步页面,以下所示:

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Home.aspx.cs" Inherits="AsyncTest.Home" Async="true" AsyncTimeout="3000" %>

这里还添加了 AsyncTimeout (可选项),请根据需求选择。

2.将此方法转换成异步方法。在这里把Thread.Sleep 与 client.DownloadString 转换成异步方法以下所示:

private async Task AsyncWork()

    {

        await Task.Delay(1000);

        WebClient client = new WebClient();

        string downloadedContent = await client.DownloadStringTaskAsync("https://msdn.microsoft.com/en-us/library/hh873175%28v=vs.110%29.aspx ");

        dvcontainer.InnerHtml = downloadedContent;
 
    }

3.如今能够直接在 Page_Load (页面加载)上调用此方法,使其异步,以下所示:

protected async void Page_Load(object sender, EventArgs e)

    {
        await AsyncWork();
    }

可是这里的 Page_Load 返回的类型是async void,这种状况不管如何都应该避免。咱们知道,Page_Load 是整个页面生命周期的一部分,若是咱们把它设置成异步,可能会出现一些异常状况和事件,好比生命周期已经执行完毕而页面加载仍在运行。 所以,强烈建议你们使用 RegisterAsyncTask 方法注册异步任务,这些异步任务会在生命周期的恰当时间执行,能够避免出现任何问题。

protected void Page_Load(object sender, EventArgs e)
    {
        RegisterAsyncTask(new PageAsyncTask(AsyncWork));
    }

如今,页面已经转换成了异步页,它就再也不是一个阻塞性请求。

笔者在 IIS8.5 上部署了同步页面和异步页面,并使用突发负载对二者进行了测试。测试结果发现,相同的机器配置,同步页面在2-3秒内只能提取1000个请求,而异步页面可以为2200多个请求提供服务。此后,开始收到超时(Timeout)或服务器不可用(Server Not Available)的错误。虽然二者的平均请求处理时间没有多大差异,可是经过异步页面,能够处理两倍以上的请求。这足以证实异步编程功能强大,因此应该充分利用它的优点。

ASP.NET中还有几个地方也能够引入异步:

  1. 编写异步模块
  2. 使用IHttpAsyncHandler 或 HttpTaskAsyncHandler 编写异步HTTP处理程序
  3. 使用web sockets 或 SignalR

##结论 本篇博文中,咱们讨论了异步编程,并且发现,新推出的async 和 await关键字,使异步编程变得十分简单。咱们讨论的话题包括 IIS和ASP.NET如何处理请求,以及在哪些场景中异步的做用最明显。另外,咱们还建立了一个简单示例,讨论了异步页面的优点。最后咱们还补充了几个ASP.NET中可使用异步的地方。

本文系 OneAPM 工程师编译呈现。OneAPM 能助您轻松锁定 .NET 应用性能瓶颈,经过强大的 Trace 记录逐层分析,直至锁定行级问题代码。以用户角度展现系统响应速度,以地域和浏览器维度统计用户使用状况。想阅读更多技术文章,请访问 OneAPM 官方博客

原文地址:http://www.infragistics.com/community/blogs/brijmishra/archive/2015/10/28/leveraging-the-power-of-asynchrony-in-asp-net.aspx

本文转自 OneAPM 官方博客

相关文章
相关标签/搜索