C#綜合揭秘——細(xì)說進(jìn)程、應(yīng)用程序域與上下文之間的關(guān)系
引言
本文主要是介紹進(jìn)程(Process)、應(yīng)用程序域(AppDomain)、.NET上下文(Context)的概念與操作。
雖然在一般的開發(fā)當(dāng)中這三者并不常用,但熟悉三者的關(guān)系,深入了解其作用,對(duì)提高系統(tǒng)的性能有莫大的幫助。
在本篇最后的一節(jié)當(dāng)中將會(huì)介紹到三者與線程之間的關(guān)系,希望對(duì)多線程開發(fā)人員能提供一定的幫助。
因?yàn)闀r(shí)間倉促,文中有錯(cuò)誤的地方敬請(qǐng)點(diǎn)評(píng)。
目錄
四、進(jìn)程應(yīng)用程序域與線程的關(guān)系
一、進(jìn)程的概念與作用
進(jìn)程(Process)是Windows系統(tǒng)中的一個(gè)基本概念,它包含著一個(gè)運(yùn)行程序所需要的資源。進(jìn)程之間是相對(duì)獨(dú)立的,一個(gè)進(jìn)程無法直接訪問另一個(gè)進(jìn)程的數(shù)據(jù)(除非利用分布式計(jì)算方式),一個(gè)進(jìn)程運(yùn)行的失敗也不會(huì)影響其他進(jìn)程的運(yùn)行,Windows系統(tǒng)就是利用進(jìn)程把工作劃分為多個(gè)獨(dú)立的區(qū)域的。進(jìn)程可以理解為一個(gè)程序的基本邊界。
1.1 Process 的屬性與方法
在 System.Diagnostics 命名空間當(dāng)中存在Process類,專門用于管理進(jìn)程的開始、結(jié)束,訪問進(jìn)程中的模塊,獲取進(jìn)程中的線程,設(shè)定進(jìn)程的優(yōu)先級(jí)別等。
表1.0 顯示了Process類的常用屬性:
| 屬性 | 說明 |
| BasePriority | 獲取關(guān)聯(lián)進(jìn)程的基本優(yōu)先級(jí)。 |
| ExitCode | 獲取關(guān)聯(lián)進(jìn)程終止時(shí)指定的值。 |
| ExitTime | 獲取關(guān)聯(lián)進(jìn)程退出的時(shí)間。 |
| Handle | 返回關(guān)聯(lián)進(jìn)程的本機(jī)句柄。 |
| HandleCount | 獲取由進(jìn)程打開的句柄數(shù)。 |
| HasExited | 獲取指示關(guān)聯(lián)進(jìn)程是否已終止的值。 |
| Id | 獲取關(guān)聯(lián)進(jìn)程的唯一標(biāo)識(shí)符。 |
| MachineName | 獲取關(guān)聯(lián)進(jìn)程正在其上運(yùn)行的計(jì)算機(jī)的名稱。 |
| MainModule | 獲取關(guān)聯(lián)進(jìn)程的主模塊。 |
| Modules | 獲取已由關(guān)聯(lián)進(jìn)程加載的模塊。 |
| PriorityClass | 獲取或設(shè)置關(guān)聯(lián)進(jìn)程的總體優(yōu)先級(jí)類別。 |
| ProcessName | 獲取該進(jìn)程的名稱。 |
| StartInfo | 獲取或設(shè)置要傳遞給Process的Start方法的屬性。 |
| StartTime | 獲取關(guān)聯(lián)進(jìn)程啟動(dòng)的時(shí)間。 |
| SynchronizingObject | 獲取或設(shè)置用于封送由于進(jìn)程退出事件而發(fā)出的事件處理程序調(diào)用的對(duì)象。 |
| Threads | 獲取在關(guān)聯(lián)進(jìn)程中運(yùn)行的一組線程 |
表1.0
除了上述屬性,Process類也定義了下列經(jīng)常使用的方法:
| 方法 | 說明 |
| GetProcessById | 創(chuàng)建新的 Process 組件,并將其與您指定的現(xiàn)有進(jìn)程資源關(guān)聯(lián)。 |
| GetProcessByName | 創(chuàng)建多個(gè)新的 Process 組件,并將其與您指定的現(xiàn)有進(jìn)程資源關(guān)聯(lián)。 |
| GetCurrentProcess | 獲取新的 Process 組件并將其與當(dāng)前活動(dòng)的進(jìn)程關(guān)聯(lián)。 |
| GetProcesses | 獲取本地計(jì)算機(jī)上正在運(yùn)行的每一個(gè)進(jìn)程列表。 |
| Start | 啟動(dòng)一個(gè)進(jìn)程。 |
| Kill | 立即停止關(guān)聯(lián)的進(jìn)程。 |
| Close | 釋放與此組件關(guān)聯(lián)的所有資源。 |
| WaitForExit | 指示 Process 組件無限期地等待關(guān)聯(lián)進(jìn)程退出。 |
表1.1
Process類的詳細(xì)信息可以參考 http://msdn.microsoft.com/zh-cn/library/system.diagnostics.process.aspx
下面將舉例介紹一下Process的使用方式
1.2 建立與銷毀進(jìn)程
利用 Start 與Kill 方法可以簡(jiǎn)單建立或者銷毀進(jìn)程,下面例子就是利用 Start 方法啟動(dòng)記事本的進(jìn)程,并打開File.txt文件。2秒鐘以后,再使用 Kill 方法銷毀進(jìn)程,并關(guān)閉記事本。
1 static void Main(string[] args)
2 {
3 Process process = Process.Start("notepad.exe","File.txt");
4 Thread.Sleep(2000);
5 process.Kill();
6 }
1.3 列舉計(jì)算機(jī)運(yùn)行中的進(jìn)程
在表1.0 中可以看到,使用 GetProcesses 方法可以獲取本地計(jì)算機(jī)上正在運(yùn)行的每一個(gè)進(jìn)程列表。
而進(jìn)程的 Id 屬性是每個(gè)進(jìn)程的唯一標(biāo)志,通過下面的方法,可以顯示當(dāng)前計(jì)算機(jī)運(yùn)行的所有進(jìn)程信息。
因?yàn)槠P(guān)系,下面例子只獲取前10個(gè)進(jìn)程。
1 static void Main(string[] args)
2 {
3 var processList = Process.GetProcesses()
4 .OrderBy(x=>x.Id)
5 .Take(10);
6 foreach (var process in processList)
7 Console.WriteLine(string.Format("ProcessId is:{0} \t ProcessName is:{1}",
8 process.Id, process.ProcessName));
9 Console.ReadKey();
10 }
運(yùn)行結(jié)果

如果已知進(jìn)程的Id,就可以通過 GetProcessById 方法獲取對(duì)應(yīng)的進(jìn)程。
1 static void Main(string[] args)
2 {
3 try
4 {
5 var process = Process.GetProcessById(1772);
6 Console.WriteLine("Process name is:" + process.ProcessName);
7 }
8 catch (ArgumentException ex)
9 {
10 Console.WriteLine("Process is nothing!");
11 }
12 Console.ReadKey();
13 }
同樣地,你也可能通過GetProcessByName方法獲取多個(gè)對(duì)應(yīng)名稱的進(jìn)程。
注意:如果不能找到當(dāng)前ID的進(jìn)程,系統(tǒng)就會(huì)拋出ArgumentException異常。所以使用方法 GetProcessById 獲取進(jìn)程時(shí)應(yīng)該包含在 try{...} catch{..} 之內(nèi)。
1.4 獲取進(jìn)程中的多個(gè)模塊
在表1.0 中包含了Process類的Modules屬性,通過此屬性可能獲取進(jìn)程中的多個(gè)模塊。
這些模塊可以是以 *.dll 結(jié)尾的程序集,也可以是 *.exe 結(jié)尾的可執(zhí)行程序。
下面的例子就是通過 Process 的 GetCurrentProcess 方法獲取當(dāng)前運(yùn)行的進(jìn)程信息,然后顯示當(dāng)前進(jìn)程的多個(gè)模塊信息。
1 static void Main(string[] args)
2 {
3 var moduleList = Process.GetCurrentProcess().Modules;
4 foreach (System.Diagnostics.ProcessModule module in moduleList)
5 Console.WriteLine(string.Format("{0}\n URL:{1}\n Version:{2}",
6 module.ModuleName,module.FileName,module.FileVersionInfo.FileVersion));
7 Console.ReadKey();
8 }
運(yùn)行結(jié)果:

二、應(yīng)用程序域
使用.NET建立的可執(zhí)行程序 *.exe,并沒有直接承載到進(jìn)程當(dāng)中,而是承載到應(yīng)用程序域(AppDomain)當(dāng)中。應(yīng)用程序域是.NET引入的一個(gè)新概念,它比進(jìn)程所占用的資源要少,可以被看作是一個(gè)輕量級(jí)的進(jìn)程。
在一個(gè)進(jìn)程中可以包含多個(gè)應(yīng)用程序域,一個(gè)應(yīng)用程序域可以裝載一個(gè)可執(zhí)行程序(*.exe)或者多個(gè)程序集(*.dll)。這樣可以使應(yīng)用程序域之間實(shí)現(xiàn)深度隔離,即使進(jìn)程中的某個(gè)應(yīng)用程序域出現(xiàn)錯(cuò)誤,也不會(huì)影響其他應(yīng)用程序域的正常運(yùn)作。
當(dāng)一個(gè)程序集同時(shí)被多個(gè)應(yīng)用程序域調(diào)用時(shí),會(huì)出現(xiàn)兩種情況:
第一種情況:CLR分別為不同的應(yīng)用程序域加載此程序集。
第二種情況:CLR把此程序集加載到所有的應(yīng)用程序域之外,并實(shí)現(xiàn)程序集共享,此情況比較特殊,被稱作為Domain Neutral。
2.1 AppDomain的屬性與方法
在System命名空間當(dāng)中就存在AppDomain類,用管理應(yīng)用程序域。下面是AppDomain類的常用屬性:
| 屬性 | 說明 |
| ActivationContext | 獲取當(dāng)前應(yīng)用程序域的激活上下文。 |
| ApplicationIdentity | 獲得應(yīng)用程序域中的應(yīng)用程序標(biāo)識(shí)。 |
| BaseDirectory | 獲取基目錄。 |
| CurrentDomain | 獲取當(dāng)前 Thread 的當(dāng)前應(yīng)用程序域。 |
| Id | 獲得一個(gè)整數(shù),該整數(shù)唯一標(biāo)識(shí)進(jìn)程中的應(yīng)用程序域。 |
| RelativeSearchPath | 獲取相對(duì)于基目錄的路徑,在此程序集沖突解決程序應(yīng)探測(cè)專用程序集。 |
| SetupInformation | 獲取此實(shí)例的應(yīng)用程序域配置信息。 |
表2.0
AppDomain類中有多個(gè)方法,可以用于創(chuàng)建一個(gè)新的應(yīng)用程序域,或者執(zhí)行應(yīng)用程序域中的應(yīng)用程序。
| 方法 | 說明 |
| CreateDomain | 創(chuàng)建新的應(yīng)用程序域。 |
| CreateInstance | 創(chuàng)建在指定程序集中定義的指定類型的新實(shí)例。 |
| CreateInstanceFrom | 創(chuàng)建在指定程序集文件中定義的指定類型的新實(shí)例。 |
| DoCallBack | 在另一個(gè)應(yīng)用程序域中執(zhí)行代碼,該應(yīng)用程序域由指定的委托標(biāo)識(shí)。 |
| ExecuteAssembly | 執(zhí)行指定文件中包含的程序集。 |
| ExecuteAssemblyByName | 執(zhí)行程序集。 |
| GetAssemblies | 獲取已加載到此應(yīng)用程序域的執(zhí)行上下文中的程序集。 |
| GetCurrentThreadId | 獲取當(dāng)前線程標(biāo)識(shí)符。 |
| GetData | 為指定名稱獲取存儲(chǔ)在當(dāng)前應(yīng)用程序域中的值。 |
| IsDefaultAppDomain | 返回一個(gè)值,指示應(yīng)用程序域是否是進(jìn)程的默認(rèn)應(yīng)用程序域。 |
| SetData | 為應(yīng)用程序域?qū)傩苑峙渲怠?/td> |
| Load | 將 Assembly 加載到此應(yīng)用程序域中。 |
| Unload | 卸載指定的應(yīng)用程序域。 |
表2.1
AppDomain類中有多個(gè)事件,用于管理應(yīng)用程序域生命周期中的不同部分。
| 事件 | 說明 |
| AssemblyLoad | 在加載程序集時(shí)發(fā)生。 |
| AssemblyResolve | 在對(duì)程序集的解析失敗時(shí)發(fā)生。 |
| DomainUnload | 在即將卸載 AppDomain 時(shí)發(fā)生。 |
| ProcessExit | 當(dāng)默認(rèn)應(yīng)用程序域的父進(jìn)程存在時(shí)發(fā)生。 |
| ReflectionOnlyAssemblyResolve | 當(dāng)程序集的解析在只反射上下文中失敗時(shí)發(fā)生。 |
| ResourceResolve | 當(dāng)資源解析因資源不是程序集中的有效鏈接資源或嵌入資源而失敗時(shí)發(fā)生。 |
| TypeResolve | 在對(duì)類型的解析失敗時(shí)發(fā)生。 |
| UnhandledException | 當(dāng)某個(gè)異常未被捕獲時(shí)出現(xiàn)。 |
表2.2
下面將舉例詳細(xì)介紹一下AppDomain的使用方式
2.2 在AppDomain中加載程序集
由表2.1中可以看到,通過CreateDomain方法可以建立一個(gè)新的應(yīng)用程序域。
下面的例子將使用CreateDomain建立一個(gè)應(yīng)用程序域,并使用Load方法加載程序集Model.dll。最后使用GetAssemblies方法,列舉此應(yīng)用程序域中的所有程序集。
1 static void Main(string[] args)
2 {
3 var appDomain = AppDomain.CreateDomain("NewAppDomain");
4 appDomain.Load("Model");
5 foreach (var assembly in appDomain.GetAssemblies())
6 Console.WriteLine(string.Format("{0}\n----------------------------",
7 assembly.FullName));
8 Console.ReadKey();
9 }
運(yùn)行結(jié)果

注意:當(dāng)加載程序集后,就無法把它從AppDomain中卸載,只能把整個(gè)AppDomain卸載。
當(dāng)需要在AppDomain加載可執(zhí)行程序時(shí),可以使用ExecuteAssembly方法。
AppDomain.ExecuteAssembly("Example.exe");
2.3 卸載AppDomain
通過Unload可以卸載AppDomain,在AppDomain卸載時(shí)將會(huì)觸發(fā)DomainUnload事件。
下面的例子中,將會(huì)使用CreateDomain建立一個(gè)名為NewAppDomain的應(yīng)用程序域。然后建立AssemblyLoad的事件處理方法,在程序集加載時(shí)顯示程序集的信息。最后建立DomainUnload事件處理方法,在AppDomain卸載時(shí)顯示卸載信息。
1 static void Main(string[] args)
2 {
3 //新建名為NewAppDomain的應(yīng)用程序域
4 AppDomain newAppDomain = AppDomain.CreateDomain("NewAppDomain");
5 //建立AssemblyLoad事件處理方法
6 newAppDomain.AssemblyLoad +=
7 (obj, e) =>
8 {
9 Console.WriteLine(string.Format("{0} is loading!", e.LoadedAssembly.GetName()));
10 };
11 //建立DomainUnload事件處理方法
12 newAppDomain.DomainUnload +=
13 (obj, e) =>
14 {
15 Console.WriteLine("NewAppDomain Unload!");
16 };
17 //加載程序集
18 newAppDomain.Load("Model");
19 //模擬操作
20 for (int n = 0; n < 5; n++)
21 Console.WriteLine(" Do Work.......!");
22 //卸載AppDomain
23 AppDomain.Unload(newAppDomain);
24 Console.ReadKey();
25 }
運(yùn)行結(jié)果

2.4 在AppDomain中建立程序集中指定類的對(duì)象
使用CreateInstance方法,能建立程序集中指定類的對(duì)像。但使用此方法將返回一個(gè)ObjectHandle對(duì)象,若要將此值轉(zhuǎn)化為原類型,可調(diào)用Unwrap方法。
下面例子會(huì)建立Model.dll程序集中的Model.Person對(duì)象。
1 namespace Test
2 {
3 public class Program
4 {
5 static void Main(string[] args)
6 {
7 var person=(Person)AppDomain.CurrentDomain
8 .CreateInstance("Model","Model.Person").Unwrap();
9 person.ID = 1;
10 person.Name = "Leslie";
11 person.Age = 29;
12 Console.WriteLine(string.Format("{0}'s age is {1}!",person.Name,person.Age));
13 Console.ReadKey();
14 }
15 }
16 }
17
18 namespace Model
19 {
20 public class Person
21 {
22 public int ID
23 {
24 get;
25 set;
26 }
27 public string Name
28 {
29 get;
30 set;
31 }
32 public int Age
33 {
34 get;
35 set;
36 }
37 }
38 }
三、深入了解.NET上下文
3.1 .NET上下文的概念
應(yīng)用程序域是進(jìn)程中承載程序集的邏輯分區(qū),在應(yīng)用程序域當(dāng)中,存在更細(xì)粒度的用于承載.NET對(duì)象的實(shí)體,那就.NET上下文Context。
所有的.NET對(duì)象都存在于上下文當(dāng)中,每個(gè)AppDomain當(dāng)中至少存在一個(gè)默認(rèn)上下文(context 0)。
一般不需要指定特定上下文的對(duì)象被稱為上下文靈活對(duì)象(context-agile),建立此對(duì)象不需要特定的操作,只需要由CLR自行管理,一般這些對(duì)象都會(huì)被建立在默認(rèn)上下文當(dāng)中。

圖3.0
3.2 透明代理
在上下文的接口當(dāng)中存在著一個(gè)消息接收器負(fù)責(zé)檢測(cè)攔截和處理信息,當(dāng)對(duì)象是MarshalByRefObject的子類的時(shí)候,CLR將會(huì)建立透明代理,實(shí)現(xiàn)對(duì)象與消息之間的轉(zhuǎn)換。
應(yīng)用程序域是CLR中資源的邊界,一般情況下,應(yīng)用程序域中的對(duì)象不能被外界的對(duì)象所訪問。而MarshalByRefObject 的功能就是允許在支持遠(yuǎn)程處理的應(yīng)用程序中跨應(yīng)用程序域邊界訪問對(duì)象,在使用.NET Remoting遠(yuǎn)程對(duì)象開發(fā)時(shí)經(jīng)常使用到的一個(gè)父類。
此文章針對(duì)的是進(jìn)程與應(yīng)用程序域的作用,關(guān)于MarshalByRefObject的使用已經(jīng)超越了本文的范圍,關(guān)于.NET Remoting 遠(yuǎn)程對(duì)象開發(fā)可參考:“回顧.NET Remoting分布式開發(fā)”。
3.3 上下文綁定
當(dāng)系統(tǒng)需要對(duì)象使用消息接收器機(jī)制的時(shí)候,即可使用ContextBoundObject類。ContextBoundObject繼承了MarshalByRefObject類,保證了它的子類都會(huì)通過透明代理被訪問。
在第一節(jié)介紹過:一般類所建立的對(duì)象為上下文靈活對(duì)象(context-agile),它們都由CLR自動(dòng)管理,可存在于任意的上下文當(dāng)中。而 ContextBoundObject 的子類所建立的對(duì)象只能在建立它的對(duì)應(yīng)上下文中正常運(yùn)行,此狀態(tài)被稱為上下文綁定。其他對(duì)象想要訪問ContextBoundObject 的子類對(duì)象時(shí),都只能通過代透明理來操作。
下面的例子,是上下文綁定對(duì)象與上下文靈活對(duì)象的一個(gè)對(duì)比。Example 是一個(gè)普通類,它的對(duì)象會(huì)運(yùn)行在默認(rèn)上下文當(dāng)中。而ContextBound類繼承了ContextBoundObject,它的對(duì)象是一個(gè)上下文綁定對(duì)象。ContextBound還有一個(gè)Synchronization特性,此特性會(huì)保證ContextBound對(duì)象被加載到一個(gè)線程安全的上下文當(dāng)中運(yùn)行。另外,Context類存在ContextProperties屬性,通過此屬性可以獲取該上下文的已有信息。
1 class Program
2 {
3 public class Example
4 {
5 public void Test()
6 {
7 ContextMessage("Example Test\n");
8 }
9 //訪問上下文綁定對(duì)象測(cè)試
10 public void Sync(ContextBound contextBound)
11 {
12 contextBound.Test("Example call on contextBound\n");
13 }
14 }
15
16 [Synchronization]
17 public class ContextBound:ContextBoundObject
18 {
19 public void Test(string message)
20 {
21 ContextMessage(message);
22 }
23 }
24
25 static void Main(string[] args)
26 {
27 Example example = new Example();
28 example.Test();
29 ContextBound contextBound = new ContextBound();
30 contextBound.Test("ContentBound Test\n");
31 example.Sync(contextBound);
32 Console.ReadKey();
33 }
34
35 //顯示上下文信息
36 public static void ContextMessage(string data)
37 {
38 Context context = Thread.CurrentContext;
39 Console.WriteLine(string.Format("{0}ContextId is {1}", data, context.ContextID));
40 foreach (var prop in context.ContextProperties)
41 Console.WriteLine(prop.Name);
42 Console.WriteLine();
43 }
44 }
運(yùn)行結(jié)果

由運(yùn)行結(jié)果可以發(fā)現(xiàn),example對(duì)象一般只會(huì)工作于默認(rèn)上下文context 0 當(dāng)中,而contextBound則會(huì)工作于線程安全的上下文 context 1當(dāng)中。當(dāng)example需要調(diào)用contextBound對(duì)象時(shí),就會(huì)通過透明代理把消息直接傳遞到context 1中。
四、進(jìn)程、應(yīng)用程序域、線程的相互關(guān)系
4.1 跨AppDomain運(yùn)行代碼
在應(yīng)用程序域之間的數(shù)據(jù)是相對(duì)獨(dú)立的,當(dāng)需要在其他AppDomain當(dāng)中執(zhí)行當(dāng)前AppDomain中的程序集代碼時(shí),可以使用CrossAppDomainDelegate委托。把CrossAppDomainDelegate委托綁定方法以后,通過AppDomain的DoCallBack方法即可執(zhí)行委托。
1 static void Main(string[] args)
2 {
3 Console.WriteLine("CurrentAppDomain start!");
4 //建立新的應(yīng)用程序域?qū)ο?/span>
5 AppDomain newAppDomain = AppDomain.CreateDomain("newAppDomain");
6 //綁定CrossAppDomainDelegate的委托方法
7 CrossAppDomainDelegate crossAppDomainDelegate=new CrossAppDomainDelegate(MyCallBack);
8 //綁定DomainUnload的事件處理方法
9 newAppDomain.DomainUnload += (obj, e) =>
10 {
11 Console.WriteLine("NewAppDomain unload!");
12 };
13 //調(diào)用委托
14 newAppDomain.DoCallBack(crossAppDomainDelegate);
15 AppDomain.Unload(newAppDomain) ;
16 Console.ReadKey();
17 }
18
19 static public void MyCallBack()
20 {
21 string name = AppDomain.CurrentDomain.FriendlyName;
22 for(int n=0;n<4;n++)
23 Console.WriteLine(string.Format( " Do work in {0}........" , name));
24 }
運(yùn)行結(jié)果

4.2 跨AppDomain的線程
線程存在于進(jìn)程當(dāng)中,它在不同的時(shí)刻可以運(yùn)行于多個(gè)不同的AppDomain當(dāng)中。它是進(jìn)程中的基本執(zhí)行單元,在進(jìn)程入口執(zhí)行的第一個(gè)線程被視為這個(gè)進(jìn)程的主線程。在.NET應(yīng)用程序中,都是以Main()方法作為入口的,當(dāng)調(diào)用此方法時(shí) 系統(tǒng)就會(huì)自動(dòng)創(chuàng)建一個(gè)主線程。線程主要是由CPU寄存器、調(diào)用棧和線程本地存儲(chǔ)器(Thread Local Storage,TLS)組成的。CPU寄存器主要記錄當(dāng)前所執(zhí)行線程的狀態(tài),調(diào)用棧主要用于維護(hù)線程所調(diào)用到的內(nèi)存與數(shù)據(jù),TLS主要用于存放線程的狀態(tài)信息。
關(guān)于線程的介紹,可參考 “C#綜合揭秘——細(xì)說多線程(上)”、“C#綜合揭秘——細(xì)說多線程(下)”
下面的例子將介紹一下如何跨AppDomain使用線程,首先建立一個(gè)ConsoleApplication項(xiàng)目,在執(zhí)行時(shí)輸入當(dāng)前線程及應(yīng)用程序域的信息,最后生成Example.exe的可執(zhí)行程序。
1 static void Main(string[] args)
2 {
3 var message = string.Format(" CurrentThreadID is:{0}\tAppDomainID is:{1}",
4 Thread.CurrentThread.ManagedThreadId, AppDomain.CurrentDomain.Id);
5 Console.WriteLine(message);
6 Console.Read();
7 }
然后再新建一個(gè)ConsoleApplication項(xiàng)目,在此項(xiàng)目中新一個(gè)AppDomain對(duì)象,在新的AppDomain中通過ExecuteAssembly方法執(zhí)行Example.exe程序。
1 static void Main(string[] args)
2 {
3 //當(dāng)前應(yīng)用程序域信息
4 Console.WriteLine("CurrentAppDomain start!");
5 ShowMessage();
6
7 //建立新的應(yīng)用程序域?qū)ο?/span>
8 AppDomain newAppDomain = AppDomain.CreateDomain("newAppDomain");
9 //在新的應(yīng)用程序域中執(zhí)行Example.exe
10 newAppDomain.ExecuteAssembly("Example.exe");
11
12 AppDomain.Unload(newAppDomain);
13 Console.ReadKey();
14 }
15
16 public static void ShowMessage()
17 {
18 var message = string.Format(" CurrentThreadID is:{0}\tAppDomainID is:{1}",
19 Thread.CurrentThread.ManagedThreadId, AppDomain.CurrentDomain.Id);
20 Console.WriteLine(message);
21 }
運(yùn)行結(jié)果

可見,ID等于9的線程在不同時(shí)間內(nèi)分別運(yùn)行于AppDomain 1與AppDomain 2當(dāng)中。
4.3 跨上下文的線程
線程既然能夠跨越AppDomain的邊界,當(dāng)然也能跨越不同的上下文。
下面這個(gè)例子中,線程將同時(shí)運(yùn)行在默認(rèn)上下文與提供安全線程的上下文中。
1 class Program
2 {
3 [Synchronization]
4 public class ContextBound : ContextBoundObject
5 {
6 public void Test()
7 {
8 ShowMessage();
9 }
10 }
11
12 static void Main(string[] args)
13 {
14 //當(dāng)前應(yīng)用程序域信息
15 Console.WriteLine("CurrentAppDomain start!");
16 ShowMessage();
17
18 //在上下文綁定對(duì)象中運(yùn)行線程
19 ContextBound contextBound = new ContextBound();
20 contextBound.Test();
21 Console.ReadKey();
22 }
23
24 public static void ShowMessage()
25 {
26 var message = string.Format(" CurrentThreadID is:{0}\tContextID is:{1}",
27 Thread.CurrentThread.ManagedThreadId, Thread.CurrentContext.ContextID);
28 Console.WriteLine(message);
29 }
30 }
運(yùn)行結(jié)果

本篇總結(jié)
進(jìn)程(Process)、線程(Thread)、應(yīng)用程序域(AppDomain)、上下文(Context)的關(guān)系如圖5.0,一個(gè)進(jìn)程內(nèi)可以包括多個(gè)應(yīng)用程序域,也有包括多個(gè)線程,線程也可以穿梭于多個(gè)應(yīng)用程序域當(dāng)中。但在同一個(gè)時(shí)刻,線程只會(huì)處于一個(gè)應(yīng)用程序域內(nèi)。線程也能穿梭于多個(gè)上下文當(dāng)中,進(jìn)行對(duì)象的調(diào)用。
雖然進(jìn)程、應(yīng)用程序域與上下文在平常的開發(fā)中并非經(jīng)常用到,但深入地了解三者的關(guān)系,熟悉其操作方式對(duì)合理利用系統(tǒng)的資源,提高系統(tǒng)的效率是非常有意義的。
尤其是三者與線程之間的關(guān)系尤為重要,特別是在一個(gè)多線程系統(tǒng)中,如果不能理清其關(guān)系而盲目使用多線程,容易造成資源搶占與死鎖之類的錯(cuò)誤。

圖5.0
希望本篇文章對(duì)相關(guān)的開發(fā)人員有所幫助。
對(duì) .NET 開發(fā)有興趣的朋友歡迎加入QQ群:162338858 共同探討 !
C#綜合揭秘
通過修改注冊(cè)表建立Windows自定義協(xié)議
Entity Framework 并發(fā)處理詳解
細(xì)說進(jìn)程、應(yīng)用程序域與上下文
細(xì)說多線程(上)
細(xì)說多線程(下)
細(xì)說事務(wù)
深入分析委托與事件
作者:風(fēng)塵浪子
http://www.rzrgm.cn/leslies2/archive/2012/03/06/2379235.html
原創(chuàng)作品,轉(zhuǎn)載時(shí)請(qǐng)注明作者及出處
posted on 2012-03-06 09:36 風(fēng)塵浪子 閱讀(49079) 評(píng)論(61) 收藏 舉報(bào)
本文主要是介紹進(jìn)程(Process)、應(yīng)用程序域(AppDomain)、.NET上下文(Context)的概念與操作。一個(gè)進(jìn)程內(nèi)可以包括多個(gè)應(yīng)用程序域,也有包括多個(gè)線程,線程也可以穿梭于多個(gè)應(yīng)用程序域當(dāng)中。但在同一個(gè)時(shí)刻,線程只會(huì)處于一個(gè)應(yīng)用程序域內(nèi)。線程也能穿梭于多個(gè)上下文當(dāng)中,進(jìn)行對(duì)象的調(diào)用。雖然在一般的開發(fā)當(dāng)中這三者并不常用,但熟悉三者的關(guān)系,深入了解其作用,對(duì)提高系統(tǒng)的性能有莫大的幫助。在本篇最后的一節(jié)當(dāng)中將會(huì)介紹到三者與線程之間的關(guān)系,希望對(duì)多線程開發(fā)人員能提供一定的幫助。
浙公網(wǎng)安備 33010602011771號(hào)