首頁 >後端開發 >C#.Net教程 >充分發揮異步在 ASP.NET 中的強大優勢

充分發揮異步在 ASP.NET 中的強大優勢

高洛峰
高洛峰原創
2016-11-21 18:01:291547瀏覽

最近幾年,非同步程式設計受到極大關注,主要是出於兩個關鍵原因:首先,它有助於提供更好的使用者體驗,因為不會阻塞UI 線程,避免了處理結束前出現UI 介面掛起。其次,它有助於大幅擴展系統,而且無需添加額外硬體。

但是,編寫合適的非同步程式碼來管理執行緒本身是項乏味的工作。雖然如此,其巨大好處讓許多新舊技術紛紛開始使用非同步程式設計。微軟自發布了 .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執行緒池允許使用同一個執行緒處理多個任務。下面來看一個描述請求處理過程的圖示。

充分發揮異步在 ASP.NET 中的強大優勢

在上圖中可以看到,請求首先由 HTTP.sys接收,並加入到對應核心級應用程式集區佇列。然後,一個IIS工作執行緒從佇列中取出請求,處理後將其傳送到ASP.NET 佇列。注意,該請求如果不是一個ASP.NET請求,將從 IIS 自動返回。最後,從CLR線程池中分配一個線程,負責處理該請求。

ASP.NET中非同步的使用場景是?

所有請求大致可以分為兩類:

CPU Bound 類

I/O Bound 類

CPU Bound 類請求,需要CPU 時間,而且是在同一進程中執行;而I/O Bound 類請求,本身俱有阻塞性,需要依賴其他模組執行I/O 操作並回傳回應。阻塞性請求是提高應用程式可擴展性的主要障礙,而且在大多數web應用程式中,在等待 I/O 操作的過程中浪費了大量時間。 因此以下場景適合使用非同步:

I/O Bound 類別請求,包括:

資料庫存取

讀/寫檔案

Web 服務呼叫

訪問網路資源

Web 服務呼叫

從多個資料來源取得資料的場景

作為範例,這裡建立一個簡單的同步頁面,然後再將它轉換成非同步頁面。 本範例設定了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;

    }

現在將該頁面轉換成非同步頁面,這裡主要涉及三步驟一:

在頁面指令中加入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中还有几个地方也可以引入异步:

编写异步模块

使用IHttpAsyncHandler 或 HttpTaskAsyncHandler 编写异步HTTP处理程序

使用web sockets 或 SignalR

结论

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


陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn