首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

深入解析 CLR 非同步程式設計模型 (APM):從 IAsyncResult 到自訂實作

     編輯:關於.NET

為什麼需要非同步 I/O?

同步 I/O 操作的特點是速度慢且不可預期。當應用程式執行同步 I/O 時,呼叫執行緒會被封鎖,無法進行其他有用的工作。若該執行緒是 UI 執行緒,整個應用程式會凍結、停止回應使用者輸入。為了提高擴展性,開發人員常建立更多執行緒,但每個執行緒都帶來核心物件、堆疊空間、內容切換等額外開銷,反而降低擴展性。

若應用程式想保持回應能力、提高擴展性與吞吐量,就不應同步執行 I/O,而應採用 Common Language Runtime (CLR) 的非同步程式設計模型 (APM) 來進行非同步 I/O 操作。

實作 APM 的四大常見場景

  1. 直接與硬體通訊:如檔案系統、網路、序列埠等。裝置 I/O 不可預期,需非同步操作以維持應用程式回應能力。
  2. 在現有硬體通訊類別上建立抽象層:例如 Web 服務用戶端代理類別、ADO.NET SqlCommand 的 BeginExecuteNonQuery 等方法,內部封裝了非同步 I/O。
  3. 提供長時間執行的計算密集型方法:雖然本質是計算密集型,但提供 BeginXxx/EndXxx 方便呼叫者使用 APM 模式。
  4. 包裝只支援同步 I/O 的 Win32 API:如登錄、事件日誌函數。若 Win32 未來提供非同步版本,只需修改包裝層,呼叫端代碼無需變動。

APM 核心:IAsyncResult 介面

CLR APM 的核心是 IAsyncResult 介面,定義四個唯讀屬性:

  • AsyncWaitHandle:用於「等待直到完成」技巧的 WaitHandle。
  • IsCompleted:用於「輪詢」技巧的布林值。
  • AsyncState:用於「回呼」技巧的使用者狀態物件。
  • CompletedSynchronously:幾乎不使用,指示是否同步完成。

呼叫 BeginXxx 時,必須內部建構實作 IAsyncResult 的物件,識別剛啟動的非同步操作狀態。呼叫端透過 EndXxx 取得結果或例外。

自訂 IAsyncResult 實作:AsyncResultNoResult 與 AsyncResult<TResult>

文章提供兩個基底類別:

  • AsyncResultNoResult:適用於無傳回值的操作(如 Stream.BeginWrite)。內部以 m_CompletedState 追蹤狀態,延遲建立 ManualResetEvent 以避免核心物件開銷。
  • AsyncResult<TResult>:繼承自 AsyncResultNoResult,增加泛型結果欄位 m_result 與對應的 SetAsCompleted/EndInvoke 多載。

關鍵設計:只有當呼叫端真正存取 AsyncWaitHandle 或在操作完成前呼叫 EndInvoke 時,才建立 ManualResetEvent。最常見的使用方式是指定 AsyncCallback,此時完全不需要事件物件。

實作範例:LongTask 類別

範例展示如何使用 AsyncResult<DateTime> 將同步方法 DoTask 包裝為非同步 BeginDoTask/EndDoTask:

  • BeginDoTask 建立 AsyncResult<DateTime>,利用執行緒池執行 DoTaskHelper。
  • DoTaskHelper 在 try 區塊呼叫同步方法,成功呼叫 SetAsCompleted(result, false),失敗呼叫 SetAsCompleted(exception, false)。
  • EndDoTask 轉型後呼叫 EndInvoke,等待完成並回傳結果或拋出例外。

三種完成通知技巧驗證

程式碼示範:

  1. 等待直到完成:直接呼叫 EndDoTask 封鎖等待。
  2. 輪詢:迴圈檢查 IsCompleted。
  3. 回呼:傳入 AsyncCallback,完成時自動觸發。

效能比較

與委派的 BeginInvoke/EndInvoke 比較,自訂實作因避免無條件建立 ManualResetEvent,效能更佳。測試 10 萬次迴圈顯示自訂實作耗時較短。

總結與展望

理解 IAsyncResult 內部運作有助於建構高效能應用程式。本文著重於計算密集型任務(使用執行緒池),後續將探討如何套用於 I/O 密集型操作。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved