在 ASP.NET MVC 中,預設的 Controller 繼承自 Controller 抽象類別,僅支援同步 Action 方法。若要實作非同步 Action,必須繼承 AsyncController。本文將詳細探討兩種非同步 Action 的定義方式,以及其底層執行機制。
ASP.NET 透過執行緒集區機制來處理並發 HTTP 請求。Web 應用程式內部維護一個工作者執行緒集區,當請求抵達時,系統從集區取得一個閒置執行緒進行處理。處理完成後,執行緒不會被銷毀,而是歸還集區以供重複使用。
若請求處理耗時較短,執行緒能快速釋放回集區。但對於長時間執行的 I/O 操作(如資料庫查詢、遠端服務呼叫),工作者執行緒會被長期佔用。高併發情況下,集區可能無可用執行緒處理新請求,導致回應延遲甚至拒絕服務。
採用非同步方式時,工作者執行緒可將耗時操作交給背景執行緒,自身迅速歸還集區處理後續請求,顯著提升吞吐能力。需特別注意的是:非同步主要適用於 I/O 繫結 操作,而非 CPU 繫結運算。因為 I/O 等待期間 CPU 可處理其他任務;若為 CPU 密集型運算,非同步反而會因執行緒切換開銷而降低效能。
此模式需定義兩個配對方法:XxxAsync 啟動非同步操作,XxxCompleted 作為完成回調呈現結果。
public class HomeController : AsyncController
{
public void ArticleAsync(string name)
{
AsyncManager.OutstandingOperations.Increment();
Task.Factory.StartNew(() =>
{
string path = ControllerContext.HttpContext.Server.MapPath(string.Format(@"\articles\{0}.html", name));
using (StreamReader reader = new StreamReader(path))
{
AsyncManager.Parameters["content"] = reader.ReadToEnd();
}
AsyncManager.OutstandingOperations.Decrement();
});
}
public ActionResult ArticleCompleted(string content)
{
return Content(content);
}
}
關鍵點:
XxxAsync,開發者必須在方法內自行實作非同步邏輯(如使用 Task、ThreadPool 或 APM 模式)。AsyncManager.OutstandingOperations.Increment/Decrement 通知框架非同步操作的開始與結束。AsyncManager.Parameters 字典傳遞資料給 XxxCompleted,鍵值需與完成方法參數名稱一致,框架會自動繫結。ASP.NET MVC 4 / .NET 4.5 起,支援直接定義回傳 Task<ActionResult> 的 Action 方法,配合 async/await 語法,程式碼更簡潔直觀:
public class HomeController : Controller
{
public async Task<ActionResult> Article(string name)
{
string path = Server.MapPath(string.Format(@"\articles\{0}.html", name));
string content;
using (StreamReader reader = new StreamReader(path))
{
content = await reader.ReadToEndAsync();
}
return Content(content);
}
}
此模式下,Controller 無需繼承 AsyncController,框架自動處理非同步執行緒調度,await 釋放執行緒,完成後自動接續執行。
無論何種定義方式,核心皆在於 釋放工作者執行緒回集區。傳統模式靠 AsyncManager 追蹤操作計數;Task 模式則由 Task 狀態機制驅動。完成時,ASP.NET MVC 從集區取得另一執行緒(或同一執行緒)繼續執行後續邏輯(如 XxxCompleted 或 await 後續程式碼),最終產生回應。
非同步 Action 是提升 ASP.NET MVC 應用程式擴展性的關鍵技術。現代開發強烈建議使用 async/await 配合 Task<ActionResult>,程式碼更易維護、除錯,且能充分發揮非同步 I/O 的效能優勢。