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

ASP.NET IHttpModule.Init 多次執行原因深度解析與正確初始化模式

     編輯:關於C#

許多開發者誤以為 IHttpModule.Init 等同於 Global.asax 的 Application_Start,並在其中執行應用程式層級的一次性初始化 (如建立靜態快取、註冊全域事件)。實際上,Init 在高併發環境下會被多次呼叫,導致重複初始化、資源洩漏或競態條件。本文深入剖析原因並提供正確解決方案。

為什麼 Init 會被多次呼叫?

ASP.NET 執行緒池模型:每個 HttpApplication 執行個體同一時間只能處理一個請求。當併發請求超過現有執行個體數時,ASP.NET 會建立新的 HttpApplication 執行個體,每個執行個體在建構時都會實例化所有已註冊的 IHttpModule 並呼叫其 Init 方法。

  • Application_Start:僅在**第一個** HttpApplication 建立後觸發一次,後續執行個體不再觸發。
  • IHttpModule.Init:**每個** HttpApplication 執行個體建立時都會觸發。

因此,若在 Init 中執行「僅需執行一次」的邏輯 (如建立單例、載入設定檔、啟動背景工作),在併發環境下就會重複執行。

驗證方式

可在 Init 中記錄 Thread.CurrentThread.ManagedThreadId 與 HttpContext.Current.ApplicationInstance.GetHashCode(),壓力測試時即可觀察到多個不同的 Application 實例觸發 Init。

正確的初始化模式

方案一:使用 Application_Start (推薦)

在 Global.asax.cs 的 Application_Start 執行一次性初始化,這是設計初衷。

方案二:在 Module 中使用靜態旗標 + 雙重檢查鎖定

public class MyModule : IHttpModule
{
    private static bool _initialized;
    private static readonly object _lock = new object();

    public void Init(HttpApplication context)
    {
        // 註冊每個請求都需綁定的事件 (如 BeginRequest)
        context.BeginRequest += OnBeginRequest;

        // 一次性初始化
        if (!_initialized)
        {
            lock (_lock)
            {
                if (!_initialized)
                {
                    PerformOneTimeInitialization();
                    _initialized = true;
                }
            }
        }
    }

    private void PerformOneTimeInitialization()
    {
        // 載入設定、建立快取、啟動排程器等
    }

    private void OnBeginRequest(object sender, EventArgs e)
    {
        // 每個請求執行的邏輯 (URL Rewrite 等)
    }

    public void Dispose() { }
}

關鍵區別

操作放在 Init放在靜態旗標區塊
註冊事件 (BeginRequest, AuthenticateRequest 等)✅ 每個 Module 實例都必須註冊❌ 只註冊一次會導致部分請求未處理
載入靜態設定、建立單例、啟動背景工作❌ 重複執行風險✅ 確保僅執行一次

進階:使用 PreApplicationStartMethod

.NET 4.0+ 可在組件層級標記 [assembly: PreApplicationStartMethod(typeof(Startup), "Initialize")],在 Application_Start **之前** 執行初始化,適合註冊動態模組、建立路由等。

總結

  • IHttpModule.Init ≠ Application_Start,前者為每個 HttpApplication 實例執行,後者為應用程式生命週期一次。
  • 事件註冊**必須**在每次 Init 執行;一次性初始化**必須**用靜態旗標保護或移至 Application_Start。
  • 誤用 Init 進行一次性初始化,在開發環境 (單一請求) 難以發現,上線後併發時才暴露詭異 Bug。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved