AppDomain是CLR實現代碼隔離的基本機制。
一個AppDomain的運行失敗不會影響到其他的AppDomain。
AppDomain被創建在進程中,一個進程內可以有多個AppDomain。一個AppDomain只能屬於一個進程。
其實兩者本來沒什麼好對比的。AppDomain是個靜態概念,只是限定了對象的邊界;線程是個動態概念,它可以運行在不同的
AppDomain。
一個AppDomain內可以創建多個線程,但是不能限定這些線程只能在本AppDomain內執行代碼。
一個soft thread只屬於一個AppDomain,穿越AppDomain的是hard thread。當hard thread訪問到某個AppDomain時,一個
當然這個說法很可能是和CLR的實現相關的。
Assembly是.Net程序的基本部署單元,它可以爲CLR提供用於識別類型的元數據等等。Assembly不能單獨執行,它必須被加載到
一個Assembly可以被多個AppDomain加載,一個AppDomain可以加載多個Assembly。
任何對象只能屬於一個AppDomain。AppDomain用來隔離對象,不同AppDomain之間的對象必須通過Proxy(reference type)或者
值類型需要設置Serializable屬性才能被Marshal/UnMarshal(Clone)。
AppDomain和程序集的源代碼是什麼關係呢?每個程序集的代碼會分別裝載到各個AppDomain中?
首先我們要把程序集分3類
1.mscorlib,這是每個.net程序都要引用到的程序集。
2.GAC,這個是強命名的公用程序集,可以被所有的.net程序引用。
3.Assembly not in GAC,這是普通的assembly,可以不是強命名,不放到GAC中。
static void Main()
{...}
LoaderOptimization Enumeration/Attribute
Value Expected Domains in Process Each Domain Expected to Run ... Code for MSCORLIB Code for Assemblies in GAC Code for Assemblies not in GAC SingleDomain One N/A Per-process Per-domain Per-domain MultiDomain Many Same Program Per-process Per-process Per-process MultiDomainHost Many Different Programs Per-process Per-process Per-domain1.SingleDomain,由於只啓動一個AppDomain,那麼code就被直接裝載到了AppDomain中,訪問靜態變量更快捷。
C#中動態加載和卸載DLL
在C++中加載和卸載DLL是一件很容易的事,LoadLibrary和FreeLibrary讓你能夠輕易的在程序中加載DLL,然後在任何地方卸載。在C#中我們也能使用Assembly.LoadFile實現動態加載DLL,但是當你試圖卸載時,你會很驚訝的發現Assembly沒有提供任何卸載的方法。這是由於託管代碼的自動垃圾回收機制會做這件事情,所以C#不提供釋放資源的函數,一切由垃圾回收來做。
這引發了一個問題,用Assembly加載的DLL可能只在程序結束的時候纔會被釋放,這也意味着在程序運行期間無法更新被加載的DLL。而這個功能在某些程序設計時是非常必要的,考慮你正在用反射機制寫一個查看DLL中所有函數詳細信息的程序,程序提供一個菜單讓用戶可以選擇DLL文件,這時就需要讓程序能夠卸載DLL,否則一旦用戶重新得到新版本DLL時,必須要重新啓動程序,重新選擇加載DLL文件,這樣的設計是用戶無法忍受的。
C#也提供了實現動態卸載DLL的方法,通過AppDomain來實現。AppDomain是一個獨立執行應用程序的環境,當AppDomain被卸載的時候,在該環境中的所有資源也將被回收。關於AppDomain的詳細資料參考MSDN。下面是使用AppDomain實現動態卸載DLL的代碼,
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Reflection;
namespace UnloadDll
{
class Program
{
static void Main(string[] args)
{
string callingDomainName = AppDomain.CurrentDomain.FriendlyName;//Thread.GetDomain().FriendlyName;
Console.WriteLine(callingDomainName);
AppDomain ad = AppDomain.CreateDomain("DLL Unload test");
ProxyObject obj = (ProxyObject)ad.CreateInstanceFromAndUnwrap(@"UnloadDll.exe", "UnloadDll.ProxyObject");
obj.LoadAssembly();
obj.Invoke("TestDll.Class1", "Test", "It's a test");
AppDomain.Unload(ad);
obj = null;
Console.ReadLine();
}
}
class ProxyObject : MarshalByRefObject
{
Assembly assembly = null;
public void LoadAssembly()
{
assembly = Assembly.LoadFile(@"TestDLL.dll");
}
public bool Invoke(string fullClassName, string methodName, params Object[] args)
{
if(assembly == null)
return false;
Type tp = assembly.GetType(fullClassName);
if (tp == null)
return false;
MethodInfo method = tp.GetMethod(methodName);
if (method == null)
return false;
Object obj = Activator.CreateInstance(tp);
method.Invoke(obj, args);
return true;
}
}
}
注意:
1. 要想讓一個對象能夠穿過AppDomain邊界,必須要繼承MarshalByRefObject類,否則無法被其他AppDomain使用。
2. 每個線程都有一個默認的AppDomain,可以通過Thread.GetDomain()來得到