1.為什么需要異步Action
池的模式一直是我們處理對象頻繁創建、銷毀時采取的一種策略。就像一個大型圖書館,當我們需要某種圖書的時候只需要到里面尋找就可以了,使用完畢之后放回;而不是每次想要獲取的時候通知印刷廠為我們印刷一本。ASP.NET對HTTP的請求處理也是采用了線程池的方式,每個web應用內部都維護着一個線程池,當請求到達之后ASP.NET會從線程池中取出一個空閑的線程來專門處理這次請求,請求結束之后線程也不是被直接銷毀而是放回到線程池供其他請求使用。需要注意的是線程池有一個最大容量如果請求數量超過這個容量ASP.NET會采取隊列排隊的方式對請求進行排隊,這個時候我們就會感覺到網頁的響應時間邊長,就像圖書館里面只有10本書現在來了20個人想要借書那么我們就讓多余的人進行排隊。
線程池的設計可以給我們帶來以下優點
· 線程重用:線程的創建和銷毀同樣需要占用服務器資源,采用線程池設計避免了頻繁的創建和銷毀進而提高服務器的吞吐能力(可以有更多的資源用於其他方面)
· 最大數量限制:最大數量限制避免了創建過多線程導致的服務器崩潰現象
如果請求的處理過程很短,線程池內的線程在使用后會被很快的重新放回線程池,這當然是最理想的狀態。但如果請求處理過程很長(比如IO處理)那么就會導致線程不能被快速使用完畢后放回至線程池,影響服務器吞吐能力。異步即為了解決這一問題,我們可以在線程池里的線程在使用時如果遇到耗時操作(主要針對IO)為其創建一個后台線程專門處理耗時操作,從而讓線程池中的線程可以盡快返還到線程池中提高服務器吞吐能力。
2.異步Action定義
在mvc3的版本中提供了定義異步Action的一種方式,即創建XxxAsync()方法和XxxCompleted()兩個方法,XxxCompleted()方法是XxxAsync()方法執行完畢之后的回調方法。ASP.NET並不會以異步的方式執行XxxAsync()方法,而是我們在XxxAsync()方法內自行實現異步。MVC4以后的版本我們可以使用Task完成異步操作。

1 public class HomeController : AsyncController 2 { 3 4 public void IndexAsync() 5 { 6 //異步執行開始標志 7 AsyncManager.OutstandingOperations.Increment(); 8 Task.Factory.StartNew(() => 9 { 10 string path = ControllerContext.HttpContext.Server.MapPath("\\Texts\\圍城.txt"); 11 using (StreamReader sr = new StreamReader(path)) 12 { 13 //回調時傳遞的參數 14 AsyncManager.Parameters["Content"] = sr.ReadToEnd(); 15 } 16 AsyncManager.Parameters["count"] = 1; 17 AsyncManager.Parameters["person"] = new Person { Name = "張三" }; 18 //異步執行結束標志 ① 19 AsyncManager.OutstandingOperations.Decrement(); 20 }); 21 //② 22 23 } 24 25 public ActionResult IndexCompleted(string content, int count,Person person) 26 { 27 return Content(content); 28 } 29 30 } 31 32 public class Person 33 { 34 public string Name { set; get; } 35 }
跟蹤執行過程
此種方式操作起來相對比較麻煩。而且一般需要借助於AcynManager對象完成異步操作。
3.AsyncManager
首先我們把代碼中標注①的代碼放到//②的位置,再跟蹤代碼會發現IndexCompleted(string content, int count,Person person)方法中的參數有時會被賦值有時為null,
這又是為什么呢?
AsyncManager.OutstandingOperations.Decrement();標志后台線程執行完畢,此時可以開始執行IndexCompleted方法,如果把它放到②位置處則只是通知IndexAsync方法執行完畢,至於Task.Factory.StartNew中的后台任務不一定執行完畢,所以出現上述情況,因此使用此種方式設計一部Action時我們需要注意AsyncManager.OutstandingOperations.Decrement();的位置。