标签:style blog http color os io 使用 strong ar
本篇主要讲述AppDomain的使用。
基础部分请看上一篇 寄宿 和 应用程序域(一)
先看一个使用AppDomain的实例1:
寄宿代码如下:
1 namespace LibraryOne 2 { 3 [Serializable] 4 public class Class1 5 { 6 public void DoSomething(int max) 7 { 8 Console.WriteLine("当前AppDomain:{0}",System.Threading.Thread.GetDomain().FriendlyName); 9 for (int i = 0; i < max; i++) 10 { 11 Console.WriteLine(i); 12 } 13 } 14 } 15 }
调用代码如下:
1 namespace MyProject 2 { 3 class Program 4 { 5 static void Main() 6 { 7 //获取当前默认AppDomain 8 AppDomain domain1 = Thread.GetDomain(); 9 10 Console.WriteLine("默认AppDomain名称:" + domain1.FriendlyName); 11 12 //创建新的AppDomain 13 //此处传递null,使domain2的权限及配置信息与当前AppDomain相同。 14 AppDomain domain2 = AppDomain.CreateDomain("domainname2", null, null); 15 16 Console.WriteLine("domain2名称:" + domain2.FriendlyName); 17 18 //加载独立程序集 19 var assembly = Assembly.LoadFrom("LibraryOne.dll"); 20 21 var s = (LibraryOne.Class1)domain2.CreateInstanceAndUnwrap(assembly.FullName, "LibraryOne.Class1"); 22 23 s.DoSomething(3); 24 25 //卸载AppDomain 26 AppDomain.Unload(domain2); 27 28 Console.ReadKey(); 29 } 30 } 31 }
结果:
可以发现,DoSomething()的执行,发生在默认AppDomain,即domain1。为什么?(后面解答)
以上是一个AppDomain使用的简单实例,其中包含了AppDomain从创建,调用,到卸载的过程。
AppDomain 创建
AppDomain提供了静态方法CreateDomain(),创建AppDomain
AppDomain.CreateDomain提供了5种重载方式,可以用于定义所创建AppDomain的 友好名称,初始化信息,权限信息,受信任程序集等信息。
AppDomain 卸载
AppDomain 提供了静态方法AppDomain.Unload(),用于卸载指定的 AppDomain 。
卸载过程大致如下:
1、CLR挂起进程中所有执行过寄宿代码的线程。
2、检查上述所有线程的线程栈,对于正在执行 或即将执行 被卸载AppDomain的线程,CLR会强迫其抛出异常ThreadAbortExecption。这将导致异常处理块finally中的代码执行,以清理资源。
如果没有异常捕捉,CLR使相关线程终止,进程继续运行。
3、当上一步所有线程都离开 被卸载AppDomain后,CLR遍历堆,为所有与 被卸载AppDomain 有关的代理设置一个标识,是这些代理知道其所引用的真实对象已经不存在。
此时,任何代码对这些代理的方法调用,都会抛出AppDomainUnloadException.
4、CLR强制垃圾回收,对已卸载AppDomain创建的任何对象进行内存回收。
5、CLR恢复剩余所有线程的执行。
对于AppDomian的调用执行,在下面实例中可以看到。
AppDomain 间通信:
AppDomain提供的主要功能就是隔离。但有时我们有需要在AppDomain间通信。
CRL提供了一些机制用于AppDomain间的通信,当然这种通信并不破坏 AppDomain的隔离性。
一、按引用封送的 跨域传值
假设AppDomain1 要与AppDomain2中的寄宿代码通信。
按引用封送的的做法是,在AppDomain2中创建寄宿代码的类型实例,当然此时不能返回该实例,返回了该实例,AppDomain的隔离就无从谈起了。
而是返回给AppDomain1一个代理,这个代理中包含了一些信息。通过这些信息,我们可以知道创建实例的AppDomain,以及如何在这个AppDomain中找到这个实例。
于是在AppDomain1中,我们通过这个代理来对AppDomain2中实例进行调用。执行时,线程会根据代理中的信息返回AppDomain2中执行具体的代码。
通过这种方式,实现了AppDomain间的通信,同时又保持了隔离的特点。
若要实现按引用封送的 跨域传值,寄宿代码必须继承类型:MarshalByRefObject
演示实例2
寄宿代码:
1 namespace LibraryOne 2 { 3 //若要实现按引用封送的 跨域传值,寄宿代码必须继承类型:MarshalByRefObject 4 public class Class2:MarshalByRefObject 5 { 6 public void DoSomething2(int max) 7 { 8 Console.WriteLine("当前AppDomain:" + System.Threading.Thread.GetDomain().FriendlyName); 9 for (int i = 0; i < max; i++) 10 { 11 Console.WriteLine(i); 12 } 13 } 14 } 15 }
调用代码:
1 namespace MyProject 2 { 3 class Program 4 { 5 static void Main() 6 { 7 //获取当前默认AppDomain 8 AppDomain domain1 = Thread.GetDomain(); 9 10 Console.WriteLine("默认AppDomain名称:" + domain1.FriendlyName); 11 12 //创建新的AppDomain 13 AppDomain domain2 = AppDomain.CreateDomain("domainname2", null, null); 14 15 Console.WriteLine("domain2名称:" + domain2.FriendlyName); 16 17 //加载独立程序集 18 var assembly = Assembly.LoadFrom("LibraryOne.dll"); 19 20 //返回一个ObjectHandle,ObjectHandle也继承了MarshalByRefObject。 21 //其值是返回的代理。代理中有成员保存了该代理实例创建自domain2,程序集为LibraryOne,类型为Class2,以及在domain2中如何找到Class2实例等信息。 22 //此创建过程在domain2中执行,代理返回到domain1 23 var s = domain2.CreateInstance(assembly.FullName, "LibraryOne.Class2"); 24 25 //返回ObjectHandle包装的对象。类型为object。值仍为代理。 26 var m = s.Unwrap(); 27 28 //类型转换,转换为Class2,值仍为代理。 29 var n = (LibraryOne.Class2)m; 30 31 //验证是否为代理 32 Console.WriteLine("Is proxy:{0}", RemotingServices.IsTransparentProxy(n)); 33 34 //调用Class2中的方法。 35 //此代码执行时,会根据代理中信息回到domain2中找到Class2实例并执行。 36 n.DoSomething2(2); 37 38 //上述代码执行完毕,仍回到当前AppDomain,即domain1。 39 Console.WriteLine("当前AppDomain名称:" + Thread.GetDomain().FriendlyName); 40 41 //卸载AppDomain 42 AppDomain.Unload(domain2); 43 44 Console.ReadKey(); 45 } 46 } 47 }
结果:
n是一个代理,DoSomething2()的执行发生在domain2中。
二、按值封送的 跨域传值
按值封送的的跨域通信,的处理方式是。结合下面实例3来看。
domain1为当前AppDomain,domain2为寄宿代码运行的AppDomain。
这里按值封送的的跨域通信的做法是,先按照按引用封送的的方式获取一个Class4实例的代理A。
Class4实例提供的方法RetrunClass3()可以返回一个 可序列化的Class3实例。
此时通过代理A,调用RetrunClass3方法,线程会返回domain2中创建一个Class3对象B。
此时,domain2向domain1返回B的引用时。CLR会将B序列化,并将序列化后的数据返回给domain1,domain1通过反序列化在domain1中得到一个B对象的复制品C。
通过这种方式,我们可以把domain2中的实例,拿到domain1中使用。此时,源实例B仍存在于domian2中。
演示实例3:
寄宿代码:
1 namespace LibraryOne 2 { 3 [Serializable] 4 public class Class3 5 { 6 public void DoSomething3(int max) 7 { 8 Console.WriteLine("当前AppDomain:" + System.Threading.Thread.GetDomain().FriendlyName); 9 for (int i = 0; i < max; i++) 10 { 11 Console.WriteLine(i); 12 } 13 } 14 } 15 public class Class4 : MarshalByRefObject 16 { 17 public Class3 class3 = null; 18 public Class3 RetrunClass3() 19 { 20 Console.WriteLine("当前AppDomain:" + System.Threading.Thread.GetDomain().FriendlyName); 21 if (class3 == null) class3 = new Class3(); 22 return class3; 23 } 24 public void DoSomething4(int max) 25 { 26 Console.WriteLine("当前AppDomain:" + System.Threading.Thread.GetDomain().FriendlyName); 27 for (int i = 0; i < max; i++) 28 { 29 Console.WriteLine(i); 30 } 31 } 32 public void CallClass3DoSomething(int max) 33 { 34 if (class3 != null) class3.DoSomething3(max); 35 } 36 } 37 38 }
调用代码:
1 namespace MyProject 2 { 3 class Program 4 { 5 static void Main() 6 { 7 //获取当前默认AppDomain 8 AppDomain domain1 = Thread.GetDomain(); 9 10 Console.WriteLine("默认AppDomain名称:" + domain1.FriendlyName); 11 12 //创建新的AppDomain 13 AppDomain domain2 = AppDomain.CreateDomain("domainname2", null, null); 14 15 Console.WriteLine("domain2名称:" + domain2.FriendlyName); 16 17 //加载独立程序集 18 var assembly = Assembly.LoadFrom("LibraryOne.dll"); 19 20 //返回一个Object 21 //其值是返回的代理。此创建过程在domain2中执行,代理返回到domain1 22 var s = domain2.CreateInstanceAndUnwrap(assembly.FullName, "LibraryOne.Class4"); 23 24 //类型转换,转换为Class4,值仍为代理。 25 var n = (LibraryOne.Class4)s; 26 27 //验证是否为代理 28 Console.WriteLine("Is proxy:{0}", RemotingServices.IsTransparentProxy(n)); 29 30 //调用Class4中的方法。此代码执行时,会根据代理中信息回到domain2中找到Class4实例并执行。 31 n.DoSomething4(4); 32 33 var c3 = n.RetrunClass3(); 34 //验证是否为代理 35 Console.WriteLine("Is proxy:{0}", RemotingServices.IsTransparentProxy(c3)); 36 c3.DoSomething3(3);//通过调用结果可以知道,此执行过程发生在domain1 37 38 n.class3.DoSomething3(2); 39 40 n.CallClass3DoSomething(5); 41 42 //上述代码执行完毕,仍回到当前AppDomain,即domain1。 43 Console.WriteLine("当前AppDomain名称:" + Thread.GetDomain().FriendlyName); 44 45 //卸载AppDomain 46 AppDomain.Unload(domain2); 47 48 Console.ReadKey(); 49 } 50 } 51 }
调用36行 c3.DoSomething3(3)后
结果:
通过输出可以看到,这个过程是在domain1中执行的。为什么?
c3是domain2中源实例在domain1中的复制品,并且是一个完整的对象,它无法穿过AppDomain去调用domain2中的源实例,只能在domain1中执行。
那么有没有方法可以执行到domain2中的源对象呢?Class4中有个源对象的实例。我们调用试试。
代码38行 n.class3.DoSomething3(2)
结果:
通过调用结果,看到任然是在domain1中执行的。为什么?
我们知道,此时n是代理,通过这个代理获取class3时,由于class3是可序列化的。返回给我们的仍然是class3的复制品,这中间仍然存在序列化反序列化的过程发生。
实际上 这个例子并不是按值封送 最直接的例子。本章的实例1 才是按值封送 最简单直接的例子。
实例1中,由于Class1是可以序列化的,通过代理对其进行调用时。会返回给宿主AppDomain一个反序列化的Class1实例,所以在实例1中,通过输出结果可以看到,执行时发生在domain1中的。
在这个例子之所以稍复杂,是为了在讲述按值封送的同时 演示两种封送方式的配合使用。
按值封送的关键是被封送的类型必须可序列化。只要通过代理去获取可序列化的对象,都会在宿主AppDomain中得到一个反序列化后的实例。
那么,我有方法可以调用到domain2中的源对象吗?
有的,但是要通过代理中的方法去调用,而不能直接通过代理去获取对象。
代码40行 n.CallClass3DoSomething(5);
结果:
看到这个输出结果,可以发现,执行时在domain2中发生的。
实际上按引用封送的,可以方便我们将domain1中的代码传入domain2中执行。
按值封送的 可以让我们将domain2中代码拿到domain1中执行。
将这两种方式结合起来,我们就可以让代码根据我们的需求穿梭于domain1和domain2之间。同时,当前线程也穿梭于两个AppDomain之间工作。
虽然演示实例中,都是domain2寄宿于domain1。实际上,我们也可以让domain1的代码寄宿于domain2执行。寄宿可以是互相的。
标签:style blog http color os io 使用 strong ar
原文地址:http://www.cnblogs.com/qingzhuo/p/3945082.html