一、深入了解.Net上下文
3.1 .NET上下文的概念
應用程序域是進程中承載程序集的路基分區,在應用程序域當中,存在更細粒度的用於承載.Net對象的實體,用於承載.Net實體的東西就叫.Net上下文Context。
所有的.Net對象都存在於上下文當中,每個AppDomain當中至少存在一個默認上下文(context 0)。
一般不需要指定特定上下文的對象被稱為上下文靈活對象(context-agile),建立此對象不需要特定的操作,只需要由CLR自行管理,一般這些對象都會被建立在默認上下文當中。
圖3.0
3.2 透明代理
在上下文的接口當中存在着一個消息接收器負責檢測攔截和處理信息,當對象是MarshalByRefObject的子類的時候,CLR將會建立透明代理,實現對象與消息之間的轉換。
應用程序域是CLR中資源的邊界,一般情況下,應用程序域中的對象不能被外界的對象所訪問。而MarshalByRefObject 的功能就是允許在支持遠程處理的應用程序中跨應用程序域邊界訪問對象,在使用.NET Remoting遠程對象開發時經常使用到的一個父類。
此文章針對的是進程與應用程序域的作用,關於MarshalByRefObject的使用已經超越了本文的范圍,關於.NET Remoting 遠程對象開發可參考:“回顧.NET Remoting分布式開發”。
3.3 上下文綁定
當系統需要對象使用消息接收器機制的時候,即可使用ContextBoundObject類。ContextBoundObject繼承了MarshalByRefObject類,保證了它的子類都會通過透明代理被訪問。
在第一節介紹過:一般類所建立的對象為上下文靈活對象(context-agile),它們都由CLR自動管理,可存在於任意的上下文當中。而 ContextBoundObject 的子類所建立的對象只能在建立它的對應上下文中正常運行,此狀態被稱為上下文綁定。其他對象想要訪問ContextBoundObject 的子類對象時,都只能通過代透明理來操作。
下面的例子,是上下文綁定對象與上下文靈活對象的一個對比。Example 是一個普通類,它的對象會運行在默認上下文當中。而ContextBound類繼承了ContextBoundObject,它的對象是一個上下文綁定對象。ContextBound還有一個Synchronization特性,此特性會保證ContextBound對象被加載到一個線程安全的上下文當中運行。另外,Context類存在ContextProperties屬性,通過此屬性可以獲取該上下文的已有信息。
二、進程、應用程序域、線程的相互關系
4.1跨AppDomain運行代碼
在應用程序域之間的數據時相互獨立的,當需要在其他AppDomain當中執行當前AppDomain中的程序集代碼時,可以使用CrossAppDomainDelegate委托。把CrocessAppDomainDelegate委托綁定方法以后,通過AppDomain的DoCallBack方法即可執行委托。
class Program { static void Main(string[] args) { Console.WriteLine("主程序域開始運行"); //建立新的應用程序域對象 AppDomain newAppDomain = AppDomain.CreateDomain("子程序域"); //綁定CrossAppDomainDelegate的委托方法 CrossAppDomainDelegate crossAppDomainDelegate = new CrossAppDomainDelegate(MyCallBack); //子應用程序域調用委托,相當於子程序域調用主程序域里的方法 newAppDomain.DoCallBack(crossAppDomainDelegate); //綁定程序域卸載事件處理方法 newAppDomain.DomainUnload += (obj, e) => { Console.WriteLine("子程序域卸載!"); }; //卸載子程序域 AppDomain.Unload(newAppDomain); Console.ReadKey(); } static public void MyCallBack() { string name = AppDomain.CurrentDomain.FriendlyName; for (int n = 0; n < 4; n++) { Console.WriteLine(string.Format("當前程序運行於{0}",name)); } } }
輸出結果如下:
4.2線程是跨AppDomain的
線程存在於進程當中,它在不同的時刻可以運行於多個不同的AppDomain當中。它是進程中的基本執行單元 ,在進程入口執行的第一個線程被視為這個進程的主線程。在.Net應用程序中,都是以Main()方法作為入口的,當調用此方法時,系統就會自動創建一個主線程。線程主要是由CPU寄存器、調用棧和線程本地存儲器(TLS)組成的。CPU寄存器主要記錄當前所執行線程的狀態,調用棧主要用於維護線程所調用到的內存與數據,TLS主要用於存放線程的狀態信息。
首先搞一個控制台程序,生成后放到D盤,代碼如下:
class Program { static void Main(string[] args) { var message = string.Format("當前線程Id 是:{0}\t 當前應用程序域是:{1}", Thread.CurrentThread.ManagedThreadId, AppDomain.CurrentDomain.Id); Console.WriteLine(message); Console.ReadKey(); } }
新建另外一個項目:
class Program { static void Main(string[] args) { //當前應用程序域信息 Console.WriteLine("主程序與開始運行!"); ShowMessage(); //建立新的應用程序域對象 AppDomain newAppDomain = AppDomain.CreateDomain("newAppDomain"); //在新的應用程序域中執行Example.exe newAppDomain.ExecuteAssembly(@"D:\ConsoleApplication1.exe"); AppDomain.Unload(newAppDomain); Console.ReadKey(); } public static void ShowMessage() { var message = string.Format("主線程Id是:{0}\t 主應用程序域是:{1}", Thread.CurrentThread.ManagedThreadId, AppDomain.CurrentDomain.Id); Console.WriteLine(message); } }
輸出結果如下:
總結:
進程(Process)、線程(Thread)、應用程序域(AppDomain)、上下文(Context)的關系如圖5.0,一個進程內可以包括多個應用程序域,也有包括多個線程,線程也可以穿梭於多個應用程序域當中。但在同一個時刻,線程只會處於一個應用程序域內。線程也能穿梭於多個上下文當中,進行對象的調用。
雖然進程、應用程序域與上下文在平常的開發中並非經常用到,但深入地了解三者的關系,熟悉其操作方式對合理利用系統的資源,提高系統的效率是非常有意義的。