我們都知道,進程是操作系統進行資源調度和分配的基本單位,每個進程實際代表了當前應用程序從啟動到結束的全部過程。對於Windows中的每一個.exe文件,在運行時都要由一個進程來承載它。與非托管.exe文件不同的是,托管.exe文件並沒有直接將程序集加載到當前進程當中,而是將程序集加載到應用程序域中,然後將應用程序域加載到進程中。每個進程可以承載多個應用程序域。因此,對於托管程序,每個進程可以承載多個應用程序,這同時也提升了應用程序的性能,因為進程的切換所耗費的性能要多於應用程序域的切換。
1.1 應用程序域的特點
托管程序為什麼要使用應用程序域呢?概括其優點如下:
1. 在一個應用程序中出現的錯誤不會影響其他應用程序。
2. 因為類型安全的代碼不會導致內存錯誤,所以使用應用程序域可以確保在一個域中運行的代碼不會影響進程中的其他應用程序。
3. 能夠在不停止整個進程的情況下停止單個應用程序。
4. 使用應用程序域時,可以卸載在單個應用程序中運行的代碼。
5. 在一個應用程序中運行的代碼不能直接訪問其他應用程序中的代碼或資源。
為了強制實施此隔離,公共語言運行庫禁止在不同應用程序域中的對象之間進行直接調用。要在各域之間傳遞對象,可以通過復制這些對象,或通過代理訪問這些對象。如果復制對象,那麼對該對象的調用為本地調用。也就是說,調用方和被引用的對象位於同一應用程序域中。如果通過代理訪問對象,那麼對該對象的調用為遠程調用。在此情況下,調用方和被引用的對象位於不同的應用程序域中。域間調用所采用的遠程調用基礎結構與兩個進程間的調用或兩台計算機間的調用的基礎結構相同。因此,被引用的對象的元數據必須對於兩個應用程序域均可用,以便用 JIT 正確編譯該方法調用。如果調用域對被調用對象的元數據沒有訪問權,則編譯可能失敗,並引發類型為 System.IO.FileNotFound 的異常。
1. 代碼行為的作用范圍由它運行所在的應用程序決定。
換言之,應用程序域將提供應用程序版本策略等配置設置、它所訪問的任意遠程程序集的位置,以及加載到該域中的程序集的位置信息。
2. 向代碼授予的權限可以由代碼運行所在的應用程序域來控制。
1.2 創建應用程序域
.NET提供了相關的類和方法來獲取當前應用程序域的基本信息,也提供了創建和配置應用程序域的成員。其中,AppDomain 類是應用程序域的編程接口,此類包括各種方法,這些方法可以創建和卸載域、創建域中各類型的實例以及注冊各種通知(如應用程序域卸載)。對於應用程序域的卸載,將在第3.1.3節做介紹。
代碼清單1-1演示了如何創建一個應用程序域。
代碼清單1-1 創建應用程序域
class Program
{
static void Main(string[] args)
{
AppDomain myDomain= AppDomain.CreateDomain("xuanhunDomain");//創建名為xuanhunDomain的應用程序域
Console.WriteLine("myDomain name is :{0}",myDomain.FriendlyName);//輸出創建的程序域名稱
Console.WriteLine("當前程序域的名稱是:{0}", AppDomain.CurrentDomain.FriendlyName);//輸出當前程序域名稱
Console.ReadKey();
}
}
輸出結果如圖1-1所示。
圖1-1 輸出應用程序域名稱
1.3 卸載應用程序域
當完成使用應用程序域時,可使用System.AppDomain.Unload方法將其卸載。Unload 方法會正常關閉指定的應用程序域。卸載過程中,沒有新線程可以訪問該應用程序域,並且會釋放該應用程序域特定的所有數據結構。加載到應用程序域中的所有程序集都會被移除,無法再使用。如果應用程序域中的程序集不是特定於域的,則程序集的數據會保留在內存中,直到整個進程關閉。除了關閉整個進程,沒有機制可以卸載非特定於域的程序集。在某些情況下,卸載應用程序域的請求不起作用,並導致CannotUnloadAppDomainException。代碼清單1-2在代碼清單1-1的基礎上添加了卸載xuanhunDomain程序域的語句,並且在卸載後再次嘗試輸出該應用程序域的名稱,這將引發CannotUnloadAppDomainException。
代碼清單1-2 卸載應用程序域
class Program
{
static void Main(string[] args)
{
AppDomain myDomain= AppDomain.CreateDomain("xuanhunDomain");
Console.WriteLine("myDomain name is :{0}",myDomain.FriendlyName);
Console.WriteLine("當前程序域的名稱是:{0}", AppDomain.CurrentDomain.FriendlyName);
AppDomain.Unload(myDomain);
try//試圖訪問被卸載的應用程序域
{
Console.WriteLine("myDomain name is :{0}", myDomain.FriendlyName);
}
catch (CannotUnloadAppDomainExceptione)
{
Console.WriteLine(e.Message);
}
Console.ReadKey();
}
輸出結果如圖1-2所示。
查看本欄目