原作者: George P. Alexander Jr. (Software Engineer) 原文發表日期: 9/1/2005
天哪, .NET Framework的CLR真是巧妙呢! 随着越來越多的對.NET底層程式設計的了解, 一些諸如架構, 處理過程的複雜難懂的細節完全地讓我歎服. 是以呢, 再次錯過我們之前忽視的細節之美是不可能的了. 有個與CLR肩并肩協同工作的一個核心元件, 叫做AppDomain, 作為.NET Framework的一部分的AppDomain是一個微軟引入的非常酷的概念.
為了更好的了解.NET的AppDomain和AppDomain是如何影響我們建立并在其上工作的程式的, 還是從頭說起比較好. 那麼讓我們從在應用程式中點選一個按鈕開始. 無論何時我們啟動一個應用程式, 我們實際上啟動了一個Win32的程序, 并且在這個程序中運作我們的程式. 這些程序使用那些諸如記憶體, 對象, 核心還有等等等等的資源. 任何一個Win32程序包含至少一個線程(越來越多, 後來就是多線程了), 并且如果我們運作其他的任務或者從我們的應用程式中打開其他的應用程式, 那麼這些任務都會屬于運作多線程集合的我們的那個Win32程序.
Win32程序有一個特點, 那就是它和虛拟邊界很相似. 在程序内通信很容易, 但是想要在某種水準上與Win32程序外通信就要受到諸多限制. 為了與其他的Win32程序通信, 我們需要一些特别的工作機制, 因為有一些安全上下文需要考慮(security contexts ), 并且還需要考慮在特定系統中, Win32程序能做什麼和不能做什麼.
那麼誰負責運作一個程序? 一個程序成功地運作都要涉及到哪些因素呢? 程序的執行以及程序中我們的代碼的運作都是在: 域(domain)和作業系統的特權下的. 在維護一個活動狀态的程序時, 作業系統不得不處理許多複雜的情況和要點.
讓我們來看一個實際的情形吧:
考慮一個如下的情形, 我們的一個伺服器内寄宿着很多個web應用程式, 或者說, 我們可能不得不在我們系統中運作一堆Windows應用程式. 現在, 瞧瞧我們正在處理什麼, 瞧瞧我們得給這些應用程式什麼吧- 資源, 資源, 甚至更多的資源! 我們的資源(記憶體)是非常昂貴的(這讓我想起了當年玩經典的id software公司的遊戲毀滅戰士的情形, 在4兆記憶體的66兆赫茲的486PC上, 我總是不得不禁用MS-Dos 6.2的smartdrv.com這個程式, 那時記憶體可是純粹的奢侈品). 并且在我們為不同客戶同時運作程式的時候, 安全問題開始出現了. 是以迫切需要我們徹底禁止任何方式的這些應用程式的程序間的通信(然而, 這總是和需求有沖突.). 這樣做會導緻經常性的崩潰(天哪, 對這樣的Windows都習以為常了!) 經常一個程序用光了被另一個程序申請來的記憶體, 然後就導緻崩潰! 我恨崩潰! 人人都恨崩潰!(當然, 通過不斷推出的新版本來維持他們的市場的微軟除外). 不管怎樣, 我們的程序很糟糕. 沒啥新鮮的, 這些頻繁的崩潰和運作時錯誤通常是由低效率的記憶體使用所引發的記憶體洩露, 對象空引用, 記憶體越界等等造成的. 是以, 大家都越來越意識到, 在一個多使用者的環境下建立, 運作和維護程序是非常非常昂貴的. 是以, 運作一大堆程序并不是個好主意, 因為他們遞增适應性不怎麼好.
在這種情況下, 一種非常精巧的解決方案應運而生. 在同一個宿主程序下運作多個應用程式可以讓我們使用更少的資源. 這甚至會導緻更快的執行速度. 但是有另一個每天都發生的場景: 一旦一個應用程式崩潰, 所有其他的在這個相同程序中的應用程式就會像一副紙牌一樣全部玩兒完!
是的, 我們說的就是這樣的多米諾骨牌效應.
那現在怎麼辦呢?
使用.NET AppDomain吧. 這個概念非常巧妙, 有新意, 配得上諾貝爾獎. 引入Application domain的主要目的, 就是将我們的應用程式與其他的應用程式隔離開. Application domains運作在一個單獨的Win32程序裡. 與我們剛剛談到的解決方案的相同, 通過在application domain内運作我們的應用程式, 來限制由記憶體洩露引起的錯誤和崩潰.
是以, 我們在一個應用程式域内運作應用程式, 并且我們在單個Win32程序中同時運作多個應用程式域. 借着CLR的運作托管代碼的能力, 我們可以進一步的減少洩露和崩潰(還要感謝CLR的垃圾收集器). 在同一個應用程式域内的對象之間直接通信, 而存活在不同的應用程式域中的對象, 如果想要彼此通信的話, 就要通過互相拷貝對象或者通過用于消息互換的代理了(通過引用).
這就是Application Domain的關鍵之處. 但是還有更多. Application Domain相當于在單Win32程序内運作的一個精巧的輕量級程序. 事實上, 如同我們上面所說, 我們可以在一個Win32程序中運作多個Application Domain. 另一個AppDomain的優勢是, 我們可以通過宿主(比如說ASP.NET)來幹掉AppDomain, 而不會影響到其他的正存在于那個程序中的AppDomain. 是以, 我們在Application domain中的工作是獨立的. 更進一步地, 我們可以通過銷毀AppDomain來解除安裝掉加載到那個AppDomain中的對象. 神奇的.NET運作時通過接管對記憶體的控制來強制使用AppDomain分隔機制, 是以Win32程序中的AppDomain裡使用的所有記憶體都由.NET運作時管理. 這樣我們就避免了剛開頭時提及的, 所有的, 諸如一個應用程式通路另一個應用程式的記憶體, 之類的問題, 也就避免了由崩潰引起的運作時錯誤問題. 是以我們實際上應用了一個安全層, 隔離了目前的應用程式, 與其他的應用程式分開. 實際上講, 在我們建立類似與運作着的web services一樣的應用程式時, Application Domain在其中扮演着一個安全方面和基礎方面的關鍵角色.
是以說我們的AppDomain應該得諾貝爾獎, .NET framework更是如此. 現在我們來看看如何建立一個基本的應用程式域.
小菜一碟, 實際上. .NET Framework提供了一個漂亮的基類, 它存在于System命名空間下, 通過它我們可以顯式地建立一個AppDomain. 在我們的應用程式中, 繼承System.MarshalByRefObject基類, 我們可以建立可以在不同應用程式域間通信的對象.
看看這個吧, 一個使用C#建立AppDomain的HelloWorld應用程式. 我們使用的是Windows 控制台程式.
【譯注:下面的代碼無法直接在Visual Studio中運作,有錯誤。MSDN上有一個差不多的,但是說明性更強的代碼,貼在本文的最後,供您參考。】
一部分AppDomain的使用方式涉及到使用Web Services的remoting. 事實上即使我們自己的.ASP.NET應用程式也是在應用程式域中建立的, 并且存在于工作者程序中(w3wp.exe).
到現在一直都還不錯. 這就好像是開始吃一個麥香堡組合, 加法式炸薯條, 香嫩多汁的内在, 完美的鹹脆的外表, 還有腦子裡想着這頓飯其他的東西更加美味可口!
【譯者注:MSDN上的關于介紹AppDomain的代碼,可以貼到Visual Studio的控制台程式裡跑跑看。】
翻譯後記: 一看筆法就知道是個老美, 言辭随意且口語化, 頗有嘻哈随意的感覺, 這種筆法寫技術文章, 并不多見, 呵呵.