<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      ASP.NET MVC下的異步Action的定義和執行原理

      Visual Studio提供的Controller創建向導默認為我們創建一個繼承自抽象類Controller的Controller類型,這樣的Controller只能定義同步Action方法。如果我們需要定義異步Action方法,必須繼承抽象類AsyncController。這篇問你講述兩種不同的異步Action的定義方法和底層執行原理。[本文已經同步到《How ASP.NET MVC Works?》中]

      目錄
      一、基于線程池的請求處理
      二、兩種異步Action方法的定義
          XxxAsync/XxxCompleted
          Task返回值
      三、AsyncManager
      四、Completed方法的執行
      五、異步操作的超時控制

      一、基于線程池的請求處理

      ASP.NET通過線程池的機制處理并發的HTTP請求。一個Web應用內部維護著一個線程池,當探測到抵達的針對本應用的請求時,會從池中獲取一個空閑的線程來處理該請求。當處理完畢,線程不會被回收,而是重新釋放到池中。線程池具有一個線程的最大容量,如果創建的線程達到這個上限并且所有的線程均被處于“忙碌”狀態,新的HTTP請求會被放入一個請求隊列以等待某個完成了請求處理任務的線程重新釋放到池中。

      我們將這些用于處理HTTP請求的線程稱為工作線程(Worker Thread),而這個縣城池自然就叫做工作線程池。ASP.NET這種基于線程池的請求處理機制主要具有如下兩個優勢:

      • 工作線程的重用:創建線程的成本雖然不如進程的激活,卻也不是一件“一蹴而就”的事情,頻繁地創建和釋放線程會對性能造成極大的損害。而線程池機制避免了總是創建新的工作線程來處理每一個請求,被創建的工作線程得到了極大地重用,并最終提高了服務器的吞吐能力。
      • 工作線程數量的限制:資源的有限性具有了服務器處理請求的能力具有一個上限,或者說某臺服務器能夠處理的請求并發量具有一個臨界點,一旦超過這個臨界點,整臺服務將會因不能提供足夠的資源而崩潰。由于采用了對工作線程數量具有良好控制的線程池機制,ASP.NET MVC并發處理的請求數量不可能超過線程池的最大允許的容量,從而避免了在高并發情況下工作線程的無限制創建而最導致整個服務器的崩潰。

      如果請求處理操作耗時較短,那么工作線程處理完畢后可以及時地被釋放到線程池中以用于對下一個請求的處理。但是對于比較耗時的操作來說,意味著工作線程將被長時間被某個請求獨占,如果這樣的操作訪問比較頻繁,在高并發的情況下意味著線程池中將可能找不到空閑的工作線程用于及時處理最新抵達請求。

      如果我們采用異步的方式來處理這樣的耗時請求,工作線程可以讓后臺線程來接手,自己可以及時地被釋放到線程池中用于進行后續請求的處理,從而提高了整個服務器的吞吐能力。值得一提的是,異步操作主要用于I/O綁定操作(比如數據庫訪問和遠程服務調用等),而非CPU綁定操作,因為異步操作對整體性能的提升來源于:當I/O設備在處理某個任務的時候,CPU可以釋放出來處理另一個任務。如果耗時操作主要依賴于本機CPU的運算,采用異步方法反而會因為線程調度和線程上下文的切換而影響整體的性能。

      二、兩種異步Action方法的定義

      在了解了在AsyncController中定義異步Action方法的必要性之后,我們來簡單介紹一下異步Action方法的定義方式。總的來說,異步Action方法具有兩種定義方式,一種是將其定義成兩個匹配的方法XxxAsync/XxxCompleted,另一種則是定義一個返回類型為Task的方法。

      XxxAsync/XxxCompleted

      如果我們使用兩個匹配的方法XxxAsync/XxxCompleted來定義異步Action,我們可以將異步操作實現在XxxAsync方法中,而將最終內容的呈現實現在XxxCompleted方法中。XxxCompleted可以看成是針對XxxAsync的回調,當定義在XxxAsync方法中的操作以異步方式執行完成后,XxxCompleted方法會被自動調用。XxxCompleted的定義方式和普通的同步Action方法比較類似。

      作為演示,我在如下一個HomeController中定義了一個名為Article的異步操作來呈現指定名稱的文章內容。我們將指定文章內容的異步讀取定義在ArticleAsync方法中,而在ArticleCompleted方法中講讀取的內容以ContentResult的形式呈現出來。

         1: public class HomeController : AsyncController
         2: {
         3:     public void ArticleAsync(string name)
         4:     {
         5:         AsyncManager.OutstandingOperations.Increment();
         6:         Task.Factory.StartNew(() =>
         7:             {
         8:                 string path = ControllerContext.HttpContext.Server.MapPath(string.Format(@"\articles\{0}.html", name));
         9:                 using (StreamReader reader = new StreamReader(path))
        10:                 {
        11:                     AsyncManager.Parameters["content"] = reader.ReadToEnd();
        12:                 }
        13:                 AsyncManager.OutstandingOperations.Decrement();
        14:             });
        15:     }
        16:     public ActionResult ArticleCompleted(string content)
        17:     {
        18:         return Content(content);
        19:     }
        20: }  

      對于以XxxAsync/XxxCompleted形式定義的異步Action方法來說,ASP.NET MVC并不會以異步的方式來調用XxxAsync方法,所以我們需要在該方法中自定義實現異步操作的執行。在上面定義的ArticleAsync方法中,我們是通過基于Task的并行編程方式來實現對文章內容的異步讀取的。當我們以XxxAsync/XxxCompleted形式定義的異步Action方法的時候,會頻繁地使用到Controller的AsyncManager屬性,該屬性返回一個類型為AsyncManager對象,我們將在下面一節對其進行單獨講述。

      在上面提供的實例中,我們在異步操作開始和結束的時候調用了AsyncManager的OutstandingOperations屬性的Increment和Decrement方法對于ASP.NET MVC發起通知。此外,我們還利用AsyncManager的Parameters屬性表示的字典來保存傳遞給ArticleCompleted方法的參數,參數在字典中的Key(content)與ArticleCompleted的參數名稱是匹配的,所以在調用方法ArticleCompleted的時候,通過AsyncManager的Parameters屬性指定的參數值將自動作為對應的參數值。

      Task返回值

      如果采用上面的異步Action定義方式,意味著我們不得不為一個Action定義兩個方法,實際上我們可以通過一個方法來完成對異步Action的定義,那就是讓Action方法返回一個代表異步操作的Task對象。上面通過XxxAsync/XxxCompleted形式定義的異步Action可以采用如下的定義方式。

         1: public class HomeController : AsyncController
         2: {
         3:     public Task<ActionResult> Article(string name)
         4:     {
         5:         return Task.Factory.StartNew(() =>
         6:             {
         7:                 string path = ControllerContext.HttpContext.Server.MapPath(string.Format(@"\articles\{0}.html", name));
         8:                 using (StreamReader reader = new StreamReader(path))
         9:                 {
        10:                     AsyncManager.Parameters["content"] = reader.ReadToEnd();
        11:                 }
        12:             }).ContinueWith<ActionResult>(task =>
        13:                 {
        14:                     string content = (string)AsyncManager.Parameters["content"];
        15:                     return Content(content);
        16:                 });
        17:     }
        18: }

      上面定義的異步Action方法Article的返回類型為Task<ActionResult>,我們將異步文件內容的讀取體現在返回的Task對象中。對文件內容呈現的回調操作則通過調用該Task對象的ContinueWith<ActionResult>方法進行注冊,該操作會在異步操作完成之后被自動調用。

      如上面的代碼片斷所示,我們依然利用AsyncManager的Parameters屬性實現參數在異步操作和回調操作之間的傳遞。其實我們也可以使用Task對象的Result屬性來實現相同的功能,Article方法的定義也改寫成如下的形式。

         1: public class HomeController : AsyncController
         2: {
         3:     public Task<ActionResult> Article(string name)
         4:     {
         5:         return Task.Factory.StartNew(() =>
         6:             {
         7:                 string path = ControllerContext.HttpContext.Server.MapPath(string.Format(@"\articles\{0}.html", name));
         8:                 using (StreamReader reader = new StreamReader(path))
         9:                 {
        10:                     return reader.ReadToEnd();
        11:                 }
        12:             }).ContinueWith<ActionResult>(task =>
        13:                 {                    
        14:                     return Content((string)task.Result);
        15:                 });
        16:     }
        17: }

      三、AsyncManager

      在上面演示的異步Action的定義中,我們通過AsyncManager實現了兩個基本的功能,即在異步操作和回調操作之間傳遞參數和向ASP.NET MVC發送異步操作開始和結束的通知。由于AsyncManager在異步Action場景中具有重要的作用,我們有必要對其進行單獨介紹,下面是AsyncManager的定義。

         1: public class AsyncManager
         2: {   
         3:     public AsyncManager();
         4:     public AsyncManager(SynchronizationContext syncContext);
         5:  
         6:     public EventHandler Finished;
         7:  
         8:     public virtual void Finish();
         9:     public virtual void Sync(Action action);
        10:     
        11:     public OperationCounter OutstandingOperations { get; }
        12:     public IDictionary<string, object> Parameters { get; }
        13:     public int Timeout { get; set; }
        14: }
        15:  
        16: public sealed class OperationCounter
        17: {
        18:     public event EventHandler Completed;    
        19:     
        20:     public int Increment();
        21:     public int Increment(int value);
        22:     public int Decrement();
        23:     public int Decrement(int value);
        24:     
        25:     public int Count { get; }
        26: }

      如上面的代碼片斷所示,AsyncManager具有兩個構造函數重載,非默認構造函數接受一個表示同步上下文的SynchronizationContext對象作為參數。如果指定的同步上下文對象為Null,并且當前的同步上下文(通過SynchronizationContext的靜態屬性Current表示)存在,則使用該上下文;否則創建一個新的同步上下文。該同步上下文用于Sync方法的執行,也就是說在該方法指定的Action委托將會在該同步上下文中以同步的方式執行。

      AsyncManager的核心是通過屬性OutstandingOperations表示的正在進行的異步操作計數器,該屬性是一個類型為OperationCounter的對象。操作計數通過只讀屬性Count表示,當我們開始和完成異步操作的時候分別調用Increment和Decrement方法作增加和介紹計數操作。Increment和Decrement各自具有兩個重載,作為整數參數value(該參數值可以是負數)表示增加或者減少的數值,如果調用無參方法,增加或者減少的數值為1。如果我們需要同時執行多個異步操作,則可以通過如下的方法來操作計數器。

         1: AsyncManager.OutstandingOperations.Increment(3);
         2:  
         3: Task.Factory.StartNew(() =>
         4: {
         5:     //異步操作1
         6:     AsyncManager.OutstandingOperations.Decrement();
         7: });
         8: Task.Factory.StartNew(() =>
         9: {
        10:     //異步操作2
        11:     AsyncManager.OutstandingOperations.Decrement();
        12: });
        13: Task.Factory.StartNew(() =>
        14: {
        15:     //異步操作3
        16:     AsyncManager.OutstandingOperations.Decrement();
        17: });

      對于每次通過Increment和Decrement方法調用引起的計數數值的改變,OperationCounter對象都會檢驗當前計數數值是否為零,如果則表明所有的操作運行完畢,如果預先注冊了Completed事件,該事件會被觸發。值得一提的時候,表明所有操作完成執行的標志是計數器的值等于零,而不是小于零,如果我們通過調用Increment和Decrement方法使計數器的值稱為一個負數,注冊的Completed事件是不會被觸發的。

      AsyncManager在初始化的時候就注冊了通過屬性OutstandingOperations表示的OperationCounter對象的Completed事件,使該事件觸發的時候調用自身的Finish方法。而虛方法Finish在AsyncManager中的默認實現又會觸發自身的Finished事件。

      如下面的代碼片斷所示,Controller類實現了IAsyncManagerContainer接口,而后者定義了一個只讀屬性AsyncManager用于提供輔助執行異步Action的AsyncManager對象,而我們在定義異步Action方法是使用的AsyncManager對象就是從抽象類Controller中集成下來的AsyncManager屬性。

         1: public abstract class Controller : ControllerBase, IAsyncManagerContainer,...
         2: {
         3:     public AsyncManager AsyncManager { get; }
         4: }
         5:  
         6: public interface IAsyncManagerContainer
         7: {    
         8:     AsyncManager AsyncManager { get; }
         9: }

      四、Completed方法的執行

      對于通過XxxAsync/XxxCompleted形式定義的異步Action,我們說回調操作XxxCompleted會在定義在XxxAsync方法中的異步操作執行結束之后被自動調用,那么XxxCompleted方法具體是如何被執行的呢?

      異步Action的執行最終是通過描述該Action的AsyncActionDescriptor對象的BeginExecute/EndExecute方法來完成的。通過之前“Model的綁定”的介紹我們知道通過XxxAsync/XxxCompleted形式定義的異步Action通過一個ReflectedAsyncActionDescriptor對象來表示的,ReflectedAsyncActionDescriptor在執行BeginExecute方法的時候會注冊Controller對象的AsyncManager的Finished事件,使該事件觸發的時候去執行Completed方法。

      也就是說針對當前Controller的AsyncManager的Finished事件的觸發標志著異步操作的結束,而此時匹配的Completed方法會被執行。由于AsyncManager的Finish方法會主動觸發該事件,所以我們可以通過調用該方法使Completed方法立即執行。由于AsyncManager的OperationCounter對象的Completed事件觸發的時候會調用Finish方法,所以當表示當前正在執行的異步操作計算器的值為零時,Completed方法也會自動被執行。

      如果我們在XxxAsync方法中通過如下的方式同時執行三個異步操作,并在每個操作完成之后調用AsyncManager的Finish方法,意味著最先完成的異步操作會導致XxxCompleted方法的執行。換句話說,當XxxCompleted方法執行的時候,可能還有兩個異步操作正在執行。

         1: AsyncManager.OutstandingOperations.Increment(3);
         2:  
         3: Task.Factory.StartNew(() =>
         4: {
         5:     //異步操作1
         6:     AsyncManager.Finish();
         7: });
         8: Task.Factory.StartNew(() =>
         9: {
        10:     //異步操作2
        11:     AsyncManager.Finish();
        12: });
        13: Task.Factory.StartNew(() =>
        14: {
        15:     //異步操作3
        16:     AsyncManager.Finish();
        17: });

      如果完全通過為完成的異步操作計數機制來控制XxxCompleted方法的執行,由于計數的檢測和Completed事件的觸發只發生在OperationCounter的Increment/Decrement方法被執行的時候,如果我們在開始和結束異步操作的時候都沒有調用這兩個方法,XxxCompleted是否會執行呢?同樣以之前定義的用語讀取/顯示文章內容的異步Action為例,我們按照如下的方式將定義在ArticleAsync方法中針對AsyncManager的OutstandingOperations屬性的Increment和Decrement方法調用注釋調用,ArticleCompleted方法是否還能正常運行呢?

         1: public class HomeController : AsyncController
         2: {
         3:     public void ArticleAsync(string name)
         4:     {
         5:         //AsyncManager.OutstandingOperations.Increment();
         6:         Task.Factory.StartNew(() =>
         7:             {
         8:                 string path = ControllerContext.HttpContext.Server.MapPath(string.Format(@"\articles\{0}.html", name));
         9:                 using (StreamReader reader = new StreamReader(path))
        10:                 {
        11:                     AsyncManager.Parameters["content"] = reader.ReadToEnd();
        12:                 }
        13:                 //AsyncManager.OutstandingOperations.Decrement();
        14:             });
        15:     }
        16:     public ActionResult ArticleCompleted(string content)
        17:     {
        18:         return Content(content);
        19:     }
        20: }

      實際上ArticleCompleted依然會被執行,但是這樣我們就不能確保正常讀取文章內容,因為ArticleCompleted方法會在ArticleAsync方法執行之后被立即執行。如果文章內容讀取是一個相對耗時的操作,表示文章內容的ArticleCompleted方法的content參數在執行的時候尚未被初始化。在這種情況下的ArticleCompleted是如何被執行的呢?

      原因和簡單,ReflectedAsyncActionDescriptor的BeginExecute方法在執行XxxAsync方法的前后會分別調用AsyncManager的OutstandingOperations屬性的Increment和Decrement方法。對于我們給出的例子來說,在執行ArticleAsync之前Increment方法被調用使計算器的值變成1,隨后ArticleAsync被執行,由于該方法以異步的方式讀取指定的文件內容,所以會立即返回。最后Decrement方法被執行使計數器的值變成0,AsyncManager的Completed事件被觸發并導致ArticleCompleted方法的執行。而此時,文件內容的讀取正在進行之中,表示文章內容的content參數自然尚未被初始化。

      ReflectedAsyncActionDescriptor這樣的執行機制也對我們使用AsyncManager提出了要求,那就是對尚未完成的一步操作計數器的增加操作不應該發生在異步線程中,如下所示的針對AsyncManager的OutstandingOperations屬性的Increment方法的定義是不對的。

         1: public class HomeController : AsyncController
         2: {
         3:     public void XxxAsync(string name)
         4:     {
         5:         Task.Factory.StartNew(() =>
         6:             {
         7:                 AsyncManager.OutstandingOperations.Increment();
         8:                   //...
         9:                   AsyncManager.OutstandingOperations.Decrement();
        10:             });
        11:     }
        12:     //其他成員
        13: } 

      下面采用正確的定義方法:

         1: public class HomeController : AsyncController
         2: {
         3:     public void XxxAsync(string name)
         4:    {
         5:        AsyncManager.OutstandingOperations.Increment();
         6:         Task.Factory.StartNew(() =>
         7:             {
         8:                 //...
         9:                 AsyncManager.OutstandingOperations.Decrement();
        10:             });
        11:     }
        12:     //其他成員
        13: }  

      最后再強調一點,不論是顯式調用AsyncManager的Finish方法,還是通過調用AsyncManager的OutstandingOperations屬性的Increment方法是計數器的值變成零,僅僅是讓XxxCompleted方法得以執行,并不能真正阻止異步操作的執行。

      五、異步操作的超時控制

      異步操作雖然適合那些相對耗時的I/O綁定型操作,但是也并不說對一步操作執行的時間沒有限制。異步超時時限通過AsyncManager的整型屬性Timeout表示,它表示超時時限的總毫秒數,其默認值為45000(45秒)。如果將Timeout屬性設置為-1,意味著異步操作執行不再具有任何時間的限制。對于以XxxAsync/XxxCompleted形式定義的異步Action來說,如果XxxAsync執行之后,在規定的超時時限中XxxCompleted沒有得到執行,一個TimeoutException會被拋出來。

      如果我們以返回類型為Task的形式定義異步Action,通過Task體現的異步操作的執行時間不受AsyncManager的Timeout屬性的限制。我們通過如下的代碼定義了一個名為Data的異步Action方法以異步的方式獲取作為Model的數據并通過默認的View呈現出來,但是異步操作中具有一個無限循環,當我們訪問該Data方法時,異步操作將會無限制地執行下去,也不會有TimeoutException異常發生。

         1: public class HomeController : AsyncController
         2: {
         3:     public Task<ActionResult> Data()
         4:     {
         5:         return Task.Factory.StartNew(() =>
         6:         {
         7:             while (true)
         8:             { }
         9:             return GetModel();
        10:                 
        11:         }).ContinueWith<ActionResult>(task =>
        12:         {
        13:             object model = task.Result;
        14:             return View(task.Result);
        15:         });
        16:     }
        17:     //其他成員
        18: }

      在ASP.NET MVC應用編程接口中具有兩個特殊的特性用于定制異步操作執行的超時時限,它們是具有如下定義的AsyncTimeoutAttributeNoAsyncTimeoutAttribute,均定義在命名空間System.Web.Mvc下。

         1: [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited=true, AllowMultiple=false)]
         2: public class AsyncTimeoutAttribute : ActionFilterAttribute
         3: {
         4:     
         5:     public AsyncTimeoutAttribute(int duration);
         6:     public override void OnActionExecuting(ActionExecutingContext filterContext);    
         7:     public int Duration { get; }
         8: }
         9:  
        10: [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited=true, AllowMultiple=false)]
        11: public sealed class NoAsyncTimeoutAttribute : AsyncTimeoutAttribute
        12: {
        13:     // Methods
        14:     public NoAsyncTimeoutAttribute() : base(-1)
        15:     {
        16:     }
        17: }

      從上面給出的定義我們可以看出這兩個特性均是ActionFilter。AsyncTimeoutAttribute的構造函數接受一個表示超時時限(以毫秒為單位)的整數作為其參數,它通過重寫OnActionExecuting方法將指定的超時時限設置給當前Controller的AsyncManager的Timeout屬性進行。NoAsyncTimeoutAttribute是AsyncTimeoutAttribute的繼承者,它將超時時限設置為-1,意味著它解除了對超時的限制。

      從應用在這兩個特性的AttributeUsageAttribute定義可看出,它們既可以應用于類也可以用于也方法,意味著我們可以將它們應用到Controller類型或者異步Action方法(僅對XxxAsync方法有效,不能應用到XxxCompleted方法上)。如果我們將它們同時應用到Controller類和Action方法上,針對方法級別的特性無疑具有更高的優先級。

      posted @ 2012-06-20 11:41  Artech  閱讀(35119)  評論(38)    收藏  舉報
      主站蜘蛛池模板: www亚洲精品| 亚洲AV蜜桃永久无码精品| 在线日韩日本国产亚洲| 国精品91人妻无码一区二区三区| 免费人妻无码不卡中文18禁| 精品尤物TV福利院在线网站| 欧洲免费一区二区三区视频| 人人妻人人狠人人爽天天综合网| 国产乱码精品一区二区上| 亚洲中文字幕综合网在线| 成人免费无遮挡无码黄漫视频| 日本视频一区二区三区1| 在线a级毛片无码免费真人| 无套内谢少妇一二三四| 国产精品自拍视频第一页| 欧美做受视频播放| 国产互换人妻xxxx69| 在线高清免费不卡全码| 娇妻玩4p被三个男人伺候| 四虎影视一区二区精品| 易门县| 啊灬啊灬啊灬快灬高潮了电影片段| 成人网站免费观看永久视频下载| 国产精品大全中文字幕| 亚洲成色在线综合网站| 国产口爆吞精在线视频2020版| 被黑人巨大一区二区三区| 亚洲a∨国产av综合av| 不卡国产一区二区三区| 自拍偷区亚洲综合第二区| 国产精品沙发午睡系列990531| 国产又色又爽又黄的| 最好看的中文字幕国语| 国产地址二永久伊甸园| 噜噜综合亚洲av中文无码| 东方四虎av在线观看| 国产欧美日韩亚洲一区二区三区| 浏阳市| 久久精品久久黄色片看看| 国产精品免费视频网站| 开心五月婷婷综合网站|