AppDomain与动态加载程序集
我们首先来了解下AppDomain 和应用程序池,我们知道在IIS上部署一个Web应用程序时,会有一个让你选择应用程序池的选择项,我们通常都喜欢用默认值,即选择DefaultApppool。我们也可以新创建个应用程序池,将需要部署的Web应用程序放到新建的应用程序池中。其实每个应用程序池就对应着一个任务管理器中的W3WP.exe进程,当你新建了N个应用程序池以后,就会有N个W3WP.exe进程,那么当你把不同的Web应用程序部署到不同的应用程序池以后,这些Web应用程序就在Process(进程)的级别上进行了隔离。那么如果我们将多个Web应用程序都部署在默认应用程序池的话,他们是如何隔离的呢?这就是在AppDomain级别进行了隔离,也就是说这些Web应用程序都运行在同一个W3WP.exe的不同的AppDomain中,这些不同的AppDomain很好的将这些Web应用程序隔离开来,使得他们不可以随便的互相访问和进行通信。下面我们来看看对于AppDomain的解释。
AppDomain是一个应用程序执行的独立环境,为执行托管代码提供隔离、卸载和安全边界. .net应用程序是由许多程序集组成的,然而不像win32程序,.net程序是在应用程序域中执行.应用程序域不同于win32的进程.实际上,一个进程可以有任意多的AppDomains,每个Appdomain之间是完全隔离的.运行在不同Appdomain中应用程序是不能共享信息的(全局变量,静态字段),除非用remoting.
服务器上的每个应用程序都在唯一的应用程序域中执行.asp.net在应用程序的生存期内维护HttpApplication实例池(应用程序的多个实例),asp.net自动指派其中的某个实例处理应用程序接收到的每个传入http请求,所指派的特定HttpApplication实例负责管理请求的整个生存期,并仅在请求完成后才被重新使用.
在应用程序域和线程之间没有一对一的关联,多个线程可以属于一个应用程序域,尽管给定的线程并不局限于一个应用程序域,但在任何给定时间,线程都在一个应用程序域中执行."(来自.NET FrameWork SDK)多线程就是同一个应用程序域会有多个线程在上面执行.
可以对AppDomain进行动态加载卸载程序集.AppDomain是程序集的执行环境,程序集作为静态实体,其可以被多个appDomain加载执行.
下面我们来研究一下动态加载程序集,有的时候,我们不想将dll添加引用到我们的应用程序集中,只希望放到某个目录下,在需要的时候动态加载它,并调用它的方法。于是我们想到了.net提供的强大的反射功能,它里面的Assembly.Load()方法可以动态的加载dll,并通过其他的反射方法来调用dll中提供的方法。这似乎和AppDomain没有任何关系,可是随着需求的变化,我们会发现,当我使用完该dll中的方法后,需要将这个dll删除掉,这时才发现,这个dll删除不掉了,系统会提示这个dll正在被使用,不可以删除。原因是该dll已经被你动态加载到当前的应用程序里面了,除非你关掉当前的应用程序才可以删除该dll.于是,我们需要做的是在使用完该dll后,将该dll动态的卸载掉,可是Assembly却不提供Unload()方法。这时我们的AppDomain可以派上用场了。下面我们来看看该如何使用AppDomain来动态加载应用程序集。
经过前面的介绍,我们已经知道AppDomain是相互隔离的,互不影响的。我们的做法就是将Dll加载到一个新的AppDomain中,然后再当前的应用程序域(AppDomain)中去远程调用新建的AppDomain中的加载的dll的方法,在调用完成后,卸载这个新建的AppDomain,而AppDomain是提供Unload方法的。原理就是如上所说的,下面来看代码:
class Program
{
static void Main(string[] args)
{
AppDomain MySampleDomain = AppDomain.CreateDomain("MySampleDomain");
Assembly ass=Assembly.LoadFile(AppDomain.CurrentDomain.BaseDirectory + "MyMarshalByRefType.dll");
Object obj = MySampleDomain.CreateInstanceAndUnwrap(ass.FullName, "MyMarshalByRefType.MyType");
//以上一行代码,将组件MyMarshalRefType.Dll加载到MySampleDomain域中,然后以下代码会在当前域中去调用组件中的方法,
//因为组件现在已经在MySampleDomain域中了,所以方法是在MySampleDomain域中执行的,也就是远程调用了不同域中的方法,
//当不需要该组件提供服务时,可以卸载MySampleDomain域,那么它当中的组件就不可以用了,但是当前域确没有卸载,可以继续其他操作,这为动态添加组件提供了方便!
Type type = obj.GetType();
MethodInfo method = type.GetMethod("MyMethod");
method.Invoke(obj, new string[] { "ddd" });
//MyMarshalByRefType.dll中的MyType类必须继承自MarshalByRefObject类,这样MyType类实例才可以在不同的AppDomain中跨域调用。
//当前示例的MyType的MyMethod方法中只有一行代码:Console.WriteLine(Thread.GetDomain().FriendlyName);
//通过示例的输出结果可以看出,MyMethod方法的确是在MySampleDomain域中被调用的。
//卸载应用程序域
AppDomain.Unload(MySampleDomain);
try
{
method.Invoke(obj, new string[] { "eeee" });
}
catch (AppDomainUnloadedException err)
{
Console.WriteLine(err.Message);
}
Console.WriteLine("please press any key....");
Console.ReadLine();
}
}
代码的相应解释都在注释中,这里就不再废话解释了。