《CLR via C#》第三版剛出版,書中關於線程章節的見解彌補了我過去的知識盲區,因此將關鍵點整理成此系列文章。作者身為微軟顧問,不僅傳授 MSDN 教條,更分享獨到見解,甚至指出 .NET 框架的不足並給出自行實現方案,這正是我推崇此書的原因。
以 32 位架構為例,每個線程包含:線程內核對象(約 700 位元組)、線程環境塊(4 KB)、內核堆疊(12 KB)以及使用者堆疊(1 MB)。值得注意的是,這 1 MB 使用者堆疊在 CLR 建立線程時一次性配置完畢,並非動態增長(Windows 僅保留 1 MB 位址空間)。
線程切換需完成三大步驟:將 CPU 暫存器值存入當前線程內核對象;若切換至不同 CPU,需準備新的虛擬位址空間;最後將目標線程內核對象中的暫存器值複製回 CPU 暫存器。
更大的效能傷害源於線程切換後,CPU 快取中的資料可能無法命中,迫使重新載入資料,這在高頻切換場景下尤為明顯。
GC 發生時,CLR 會暫停所有線程、掃描線程堆疊、壓縮堆疊並重設指標位址,線程越多,暫停時間越長。
雖然線程比進程輕量,但 Outlook 就有數十個線程,甚至記事本的「開啟檔案」對話框也能產生 22 個線程(Vista 為避免阻塞 UI,大量使用新線程載入內容)。目前 CLR 線程一對一對應 Windows 線程,作者期盼未來 CLR 能實現虛擬邏輯線程以改善效能。