過去 ASP.NET MVC 4 前為了撰寫非同步控制器 (AsyncController)
Controller 都必須繼承 AsyncController
但時代在變微軟在進化 ASP.NET MVC 4 之後,AsyncController 就被微軟閹割
未來要寫非同步控制器(Asynchronous Controllers)就更簡單
async/await 搭配 Task 物件的組合就可以達成
IIS TreadPool 不是就類似非同步處理了?
IIS 本身就有一個 TreadPool 來處理用戶端 Request Queue
- 應用程式集區 > 你的應用程式AppPool > 進階設定 > 佇列長度
- 佇列長度預設: 1,000 (.NET 4.5 預設最大值為5,000)
- 佇列長度1,000實際大約只能處理 650 個併發用戶端,之後的請求皆回應 “HTTP 503 服務無法使用”
盲目的加大佇列長度,有可能會導致導致大量的記憶體耗用量和伺服器硬體的使用率不佳
每一條新的執行緒就要配置約1MB堆疊記憶體空間至TreadPool,如果佇列長度為5,000,大約消耗5GB的記憶體空間,可能會造成IIS 執行緒耗盡(TreadPool)
使用非同步控制器
使用非同步控制器(Asynchronous Controllers)目的為解決以上的問題
讓 Controller 接收到用戶端 Request 後,暫時把 Thread 歸還給 IIS
這樣就不會佔用 IIS 的 ThreadPool,保持通訊暢通
等到 Controller 執行完畢再去跟 IIS ThreadPool 請求另一條 Thread 來回應執行結果給用戶端
但也不是所有的 Controller 都要用非同步方式處理
應該要觀察你的 Controller 實作內容
使用同步方法的情境
- 程式處理邏輯很單純
- 程式執行時間很短暫
- 程式運算主要都是 CPU 運算,而非需要耗用大量磁碟I/O或網路傳輸
-
public ActionResult Index() { ViewBag.SyncOrAsync = "Synchronous"; var userService = new UserService(); return View("User", userService.GetUser()); }
使用非同步方法的情境
- 程式運算需要耗用大量磁碟I/O或網路傳輸,而非 CPU運算
- 可讓用戶端取消長期執行要求的需求
- 當切換執行緒的好處高於內容切換的成本時
-
public async Task<ActionResult> Index() { ViewBag.SyncOrAsync = "Asynchronous"; var userService = new UserService(); return View("User", await userService.GetUserAsync()); }
Controller 加上 async關鍵字,就為宣告該Action為非同步
方法中有一個 Await關鍵字為等待 GetUserAsync() 處理完才做回傳 Task<ActionResult>
適當的應用非同步可以讓系統整體運作更有效率,用戶端用起來也會更順暢
參考資料