實作 IHttpHandler 時,IsReusable 屬性常被誤解。本文從 ASP.NET 請求處理管線、Handler Factory 建立實例機制、物件池概念切入,徹底釐清 IsReusable=true/false 的行為差異、適用場景、執行緒安全風險與效能影響,並給出決策流程圖。
HttpRuntime。HttpHandlerFactory (如 PageHandlerFactory) 依 web.config 對應建立 IHttpHandler 實例。handler.ProcessRequest(context)。| 值 | 行為 | 實例生命週期 | 適用場景 |
|---|---|---|---|
true | **單一實例處理多個請求** (類單例) | 應用程式域存活期間 (或 AppDomain 卸載前) | 無狀態、純計算、靜態資源輸出 (如圖片水印、動態 CSS/JS、API 端點) |
false (預設) | **每請求建立新實例** | 單一請求 | 有狀態、持有請求相關資料 (Session、User、交易資源)、非執行緒安全的邏輯 |
true 意味著**同一實例可能被多執行緒同時呼叫 ProcessRequest**。必須確保:
context 參數已提供所有需求)。ConcurrentDictionary、連線池)。ProcessRequest 中修改實例狀態。// ✅ 正確:無狀態 Handler
public class WatermarkHandler : IHttpHandler
{
public bool IsReusable => true; // 安全:無實例狀態
public void ProcessRequest(HttpContext context)
{
// 純從 context 取參數、讀圖、加浮水印、輸出
// 所有資料在區域變數,天然執行緒安全
}
}
// ❌ 錯誤:有狀態 Handler 卻設 true
public class BadHandler : IHttpHandler
{
private string _lastUser; // 實例欄位!執行緒不安全
public bool IsReusable => true;
public void ProcessRequest(HttpContext context)
{
_lastUser = context.User.Identity.Name; // 競態條件!
// ...
}
}
| 指標 | IsReusable=false (每請求新建) | IsReusable=true (單例重用) |
|---|---|---|
| 物件配置/GC 壓力 | 高 (QPS 成正比) | 極低 (僅啟動時 1 次) |
| 啟動延遲 (冷啟動) | 每請求均有 | 僅首次 |
| 記憶體基線 | 較低 (請求結束可回收) | 略高 (實例長駐) |
| 適用 QPS | < 1000 (視 Handler 複雜度) | 10,000+ (無鎖競爭前提) |
Handler 是否持有「請求相關」實例狀態 (欄位、屬性)?
├── 是 → IsReusable = false (預設,安全第一)
└── 否 → 邏輯是否完全執行緒安全 (無共享可變狀態)?
├── 否 → IsReusable = false
└── 是 → 預期 QPS 高、延遲敏感?
├── 是 → IsReusable = true (並做好壓測驗證)
└── 否 → false 亦可 (簡單、無風險)
IsReusable=true 等同單例模式 → 不完全是,ASP.NET 可能建立多個實例 (如多應用程式域、Web Garden、重啟)。true 就能共享資料庫連線 → 連線池已在 ADO.NET 層處理,Handler 層無需也不可持有連線。ashx 泛型處理常式預設 true → 實際預設 false,需显式覆寫。IsReusable 是效能與安全的權衡開關。**預設 false 最安全**,無狀態高頻 Handler 經嚴格執行緒安全驗證後可開啟 true 獲取極致吞吐。切記:Handler 實例欄位 = 請求狀態 = 重用大忌。寫出無狀態的 ProcessRequest,才是高效能 HttpHandler 的王道。