顯示具有 multi-thread 標籤的文章。 顯示所有文章
顯示具有 multi-thread 標籤的文章。 顯示所有文章

2009年2月22日

無法預測、無法控制的 ASP.NET multi-thread 機制

所謂的 thread 是甚麼相信大家都知道,多少也在學生時代寫過一兩個多執行緒的作業。
在 .NET 的世界裡面,無論是Web Apps or Windows Apps,在 runtime 時期都是在 CLR 上運作,而 CLR 所看到的都是在 AppDomain 中執行的 thread,
Web/Windows Apps 的分別只是在於 CLR 載入的 aassembly 不同罷了。

從上上週末開始,我們 Team 就開始設法利用 TLS 來解決舊元件造成的 MSDTC 問題,
在想像中這個 solution 可以同時滿足 Web Apps & Windows Apps ,
沒想到就此一頭栽進了深不見底的 ASP.NET multi-thread 地獄。
(到現在還是處於一頭霧水的狀態 Orz)


經過5個系統的實測(4個是各部門的系統,另一個是自己寫的超小型 demo 網站),
我們發現「ASP.NET 在某些不明條件下,會以 multi-thread 的方式來處理 Request」,
也就是「custom httpModule 與 page 的 code-behind 是以不同的 Thread 在運作」,
這與我們想像中的「ASP.NET 對一個 Request 從頭到尾都是以單一 Thread 來處理」的假設完全不同,而這種現象使得 TLS Solution 呈現完全報廢的狀態,為甚麼呢?

What’s ThreadLocalStorage 這篇文章中所附的範例程式中,我是在一個自己撰寫的 custom httpModule 中去處理 TLS 中的資料,若 ASP.NET 的行為一如預期的是以單一 Thread 來處理 Request,那麼在page 的 code-behind 程式 中便可順利取得在 custom httpModule 中存入 TLS 的資料(connection & transaction 物件),也就可以執行 local transactions 而不需勞師動眾的啟動 distributed transaction (MSDTC) 了。

但是當 ASP.NET 以新的 Thread (#2) 來處理 page 的 code-behind 程式時,Thread #2 是無法取得 custom httpModule (Thread #1) 的 TLS 中的 connection & trnasaction 物件的(不然怎麼叫 ThreadLocalStorage 呢?),因此在 code-behind 的程式中要 access DB 時便會出現 NullReferenceException

奇怪的是,在拿來實測的系統中完全沒有撰寫 multi-thread 的程式(e.g., Thread.Start()),因此目前推測 multi-thread 現象是 ASP.NET 內部所做的 (Performance) Optimization 所產生的結果,是我們的程式無法(起碼很難)控制的。

在5個實測過的系統中,出現以下的 multi-thread 現象:
(System E 是自己寫的超小型 demo 網站)
  • 有插入 custom httpModule:
    • System A: httpModule 屬於 Thread #1,page 屬於 Thread #2
    • System B: httpModule 屬於 Thread #1 & #2,page 屬於 Thread #3 ~ #n
    • System C: httpModule 與 page 屬於 Thread #1 (使用單一 Thread!)
    • System D: httpModule 屬於 Thread #1,page 屬於 Thread #2
    • System E: httpModule 與 page 屬於 Thread #1 (使用單一 Thread!)
  • 插入 custom httpModule (也就是一般寫網站的狀況)
    • System A: page 屬於 Thread #1 ~ #n
    • System B: page 屬於 Thread #1 (使用單一 Thread!)
    • System C: 沒有測到
    • System D: page 屬於 Thread #1 (使用單一 Thread!)
    • System E: httpModule 與 page 屬於 Thread #1 (使用單一 Thread!)
根據上述現象,目前推測與 ASP.NET multi-thread 機制無關的因素包括:
  • .NET Framework 版本 (1.1/2.0/3.5)
  • IIS 版本 (6.0/7.0)
  • 是否(混合)使用 MasterPage、UserControl、CustomControl
  • Page 是否繼承自己包的 BasePage
  • 頁面是否有用 frame 切割
而推測可能有關的因素包括:
  • 某種程式寫法會造成 ASP.NET 內部自動改以 multi-thread 來執行
    • 在 page 中以 js 另開視窗(做一些事情)之後再關閉的程式寫法
    • 在自己寫的超小型 demo 網站中,MasterPage、BasePage、UserControl 中的 Init / OnLoad 事件中的程式碼都是空的,不會啟動 multi-thread
  • 註冊 custom httpModule (不註冊的話,使用單一 Thread 的機率高很多)
  • 第一次 Request 與 (Ctrl+) F5 Refresh 會造成不同的效果 (不同數量的 Thread)
雖然目前還是不清楚 ASP.NET 啟動 multi-thread 機制的條件為何,但已經很明顯得到一個「TLS 此路不通」的結論,目前也沒找到比較深入的官方 reference(不過倒是曾經聽過在 ASP.NET 中最好不要自己寫 multi-thread 的程式的說法),因此這個有趣的謎只能等有空再研究囉~~

那麼要如何觀測系統是否有啟動 multi-thread 呢?以下是觀測的步驟,有興趣的人可以拿自己的系統試試看:
  1. 進入 Visual Studio 的 Debug Mode。或者先以 IIS/ASP.NET Development Server (lightweight 的那個)瀏覽到目標頁面後,將 Visual Studio 附加至該網頁的處理序上進行 debug。
  2. 在該 aspx 以及 BasePage、MasterPage、UserControl 等檔案的 code-behind 程式中的 Init / OnLoad 事件上設定中斷點。
  3. 觀察執行過程中「System.Threading.Thread.CurrentThread.ManagedThreadId」的值。

2009年2月8日

What’s ThreadLocalStorage (aka TLS)?

最近開始試圖解決公司底層元件的宿疾陳痾 -- 惡名昭彰的 MSDTC
以使用 TransactionScope 來說,為了確保系統中全程採用 Local Transaction,
重點在於在交易過程中必須使用單一的 connection,且不可以有開開關關的動作
 
(關於 MSDTC 的說明請參考附註中 Darkthread 大大的.NET分散式交易程式開發FAQ)

但是很不幸的,舊的底層元件(Entity)為了簡化外部程式的寫法, 主動把 connection 物件給包了起來,並且在每個 public 的 CRUD Method 中去 open & close connection,因此只要在 TransactionScope 中使用多個 Entity, 或者先呼叫某個 Entity 的 Select 再呼叫 Save(造成 connection 開關多次), 就會導致該交易從 Local Transaction 被 automatic escalate 為 MSDTC, 進而造成 MA 時莫大的痛苦

(幾乎每次 MSDTC 出錯時,根據當時的 System Configuration 都可以在網路上找到相對應的案例。也就是說,各種可能導致 MSDTC 出錯的環境組合幾乎是無窮盡的 =.=”)


為了在「避免舊系統大幅改寫」的前提下,修改底層元件的寫法以設法避免 MSDTC,
我們需要一個 Global 的地方以存放共用的 connection (以及相關的交易控制資料),
在一番討論 & survey 之後,決定採用 ThreadLocalStorage (簡稱 TLS) 這個技術。

以下是 TLS 的基本介紹:
  • TLS 的技術可以回溯到 Win32 multi-threaded programming,在 .NET 的世界中也有一套相對應的作法。
  • TLS 的基本原理是,在每個 thread 中分配一塊可以用來儲存資料的地方,只要在該 thread 中執行的 method 均可存取這些資料。
  • TLS 的一個好用之處,在於可在不修改現有程式碼(不管是改不動還是根本就沒有 source code)的情況下,在系統中增加(效能分析)用的 tracking information(有一點 AOP 的感覺)。對我目前面臨的問題來說,剛好可以當作 Global Data Storage 來儲存 connection 物件。
  • 在 TLS 中,一份資料對於某一組「thread + AppDomain」來說是 unique 的。TLS 不能像 .NET Remoting 技術那樣的 cross AppDomain boundary。
    (.NET Remoting 還蠻複雜的,看來沒時間仔細研究。不過以後會被 WCF 取代)
    image
  • 儲存於 TLS 的資料一律是 Object 的資料型態,因此必須自己 handle 轉型。
TLS 的基本程式寫法很簡單,可以參考這裡。雖然目前這個方法還沒有實際驗證過,
但根據手上的資料來看應該是可以解決 MSDTC 的問題。

我的小小 TLS 範例程式可以在這裡下載。

PS. 關於 MSDTC 的說明,可參考 Darkthread 大大在 Run!PC 上發表的文章,如果忘記這個連結位置的話,可以 google「darkthread TransactionScope」,在第二個 result「Browse by Tags - Darkthread」中就可以找到「.NET分散式交易程式開發FAQ」。

Google Spreadsheet 裡用規則運算式

最近因為工作關係,遇到要用 Google Form 及 Google Sheet 所以研究了 Google Sheet 裡的一些 function 怎麼用 首先,分享一下如何在 Google Sheet 裡用規則運算 :D