AppDomain浅析与实例

来源:互联网 发布:股票付费软件排名 编辑:程序博客网 时间:2024/06/04 17:55
    我们知道,进程是操作系统用于隔离众多正在运行的应用程序的机制。在.Net之前,每一个应用程序被加载到单独的进程中,并为该进程指定私有的虚拟内存。进程不能直接访问物理内存,操作系统通过其它的处理把这些虚拟内存映射到物理内存或IO设备的某个区域,而这些物理内存之间不会有重叠,这就决定了一个进程不可能访问分配给另一个进程的内存。相应地,运行在该进程中的应用程序也不可能写入另一个应用程序的内存,这确保了任何执行出错的代码不会损害其地址空间以外的应用程序。在这种机制下,进程作为应用程序之间一个独立而安全的边界在很大程度上提高了运行安全。进程的缺点是降低了性能。许多一起工作的进程需要相互通信,而进程却不能共享任何内存,你不能通过任何有意义的方式使用从一个进程传递到另一个进程的内存指针。此外,你不能在两个进程间进行直接调用。你必须代之以使用代理,它提供一定程度的间接性。虽然,使用动态连接库dll让所有的组件运行在同一空间,一定程度上可以提高性能,但这些组件相互影响,一个组件的错误将极有可能导致整个应用程序的崩溃,“dll地狱”更是让许多应用程序难以避免。应用程序域(AppDomain) 在.Net中,应用程序有了一个新的边界:应用程序域(以下简称域)。它是一个用于隔离应用程序的虚拟边界。为了禁止不应交互的代码进行交互,这种隔离是必要的。.Net的应用程序在域层次上进行隔离,一个域中的应用程序不能直接访问另一个域中的代码和数据。这种隔离使得在一个应用程序范围内创建的所有对象都在一个域内创建,确保在同一进程中一个域内运行的代码不会影响其他域内的应用程序,大大提高了运行的安全。.Net结构中,由于公共语言运行库能够验证代码是否为类型安全的代码,所以它可以提供与进程边界一样大的隔离级别,其性能开销也要低得多。你可以在单个进程中运行几个域,而不会造成进程间调用或切换等方面的额外开销。这种方法是把任何一个进程分解到多个域中,允许多个应用程序在同一进程中运行,每个域大致对应一个应用程序,运行的每个线程都在一个特殊的域中。如果不同的可执行文件都运行在同一个进程空间中,它们就能轻松地共享数据或直接访问彼此的数据。这种代码同运行同一个进程但域不同的类型安全代码一起运行时是安全的。在一个进程内运行多个应用程序的能力显著增强了服务器的可伸缩性。域间通信域是.Net带来的一个重要改进,它不仅将众多在运行的应用程序隔离开来,还不影响彼此间通信。虽然,公共语言运行库禁止在不同域中的对象之间进行直接调用,但我们可以复制这些对象,或通过代理访问这些对象。如果以前一种方式,那么对该对象的调用为本地调用。也就是说,调用方和被引用的对象位于同一域中。如果通过代理访问对象,调用方和被引用的对象位于不同的域中,对该对象的调用被视为远程调用,这种情形与两个进程间的调用或两台计算机间的调用结构大致相同。这时,需要被引用对象的元数据对于两个域均可用,以便.Net即时编译JIT能正确执行。域与线程的关系在.Net中,线程是公共语言运行库用来执行代码的操作系统构造。在运行时,所有托管代码均加载到一个域中,由特定的操作系统线程来运行。然而,域和线程之间并不具有一一对应关系。在任意给定时间,单个域中可以执行不止一个线程,而且特定线程也并不局限在单个域内。也就是说,线程可以跨越域边界,不为每个域创建新线程。当然,在指定时刻,每一线程都只能在一个域中执行。运行库会跟踪所有域中有哪些线程正在运行。通过调用.Net类库的 Thread.GetDomain 方法,你还可以确定正在执行的线程所在的域。域的创建作为公共语言运行库的隔离单元,域在进程中创建和运行。.Net结构中,运行时宿主(也叫作运行时主机)是负责将运行时载入进程并在域中执行用户代码和托管代码的应用程序。运行时宿主包括ASP.Net、浏览器InternetExplorer 和Windows等外壳程序,负责创建进程和默认域,例如,Asp.Net为每个运行在web服务器上的web应用程序创建一个域。浏览器Internetexplore创建运行受管制控件的域。对多数应用程序,你并不必须创建相应的域,每次CLR在初始化一个进程时,将创建默认域,并使该进程运行于这个默认域下。然而,默认域不能由任何系统调用来卸载,该域只有在进程被卸载之后才能被销毁。如果直接在默认域下编程或运行代码,而由于某种原因域的代码崩溃了,那么就有使得整个服务随之崩溃的风险。于是,针对不同的应用程序,应该创建和配置相应的域并载入适当的程序集。.Net为此提供了丰富的类库。其中,AppDomain 类是域的编程接口,其大量的(重载)方法能完成以下任务:· 创建域· 在域中加载程序集和类型· 枚举域中的程序集和线程· 卸载域创建新域时,使用AppDomain 类的静态方法CreateDomain。你可以为域命名并按该名称来引用域。下面的示例语句创建新域,并为它指定名称 MyDomain: AppDomain myDomain = AppDomain.CreateDomain("MyDomain"); 然后你可以查询当前域的名称和新创建子域的名称: string hostDomain=AppDomain.CurrentDomain.FriendlyName; string childDomain=myDomain.FriendlyName; 在这里,属性FriendlyName表示的是域的友好名称,友好名称通过从程序集的基本代码中去除目录路径而形成。例如,文件名为 "d:/MyAppDomain/MyAssembly.exe"的程序集加载到默认域中,域的友好名称就是 "MyAssembly.exe"。更一般的是,在创建域之前,先设置好域的参数,这可以通过类AppDomainSetup来完成。该类的ApplicationBase 属性定义应用程序的根目录, AppDomainSetup类还有一个极重要的属性变量LoaderOptimizzation,取值可以是MultiDomain,MultiDomainHost和SignleDomain等,用以指定被加载程序集的类别(共享程序集或域专用程序集),例如,以下语句把程序集设置为域专用程序集: appDomainSetup.LoaderOptimization=LoaderOptimizatiion.SigleDomain; 对以上两个方面简单归纳一下,对域的典型操作就包括:设置参数然后创建两个步骤,语句示例如下: AppDomainSetup appDomainSetup=new AppDomainSetup();//实例化域设置appDomainSetup.LoaderOptimization=LoaderOptimization.SingleDomain; //指定域类别AppDoman ad=AppDomain.CreateDomain(domainName,appDomainSetup); //创建域...//应用程序在这里运行代码...AppDomain.Unload(ad);//卸载域 卸载域当使用完域时,可使用AppDomain类Unload()静态方法将其卸载。要卸载进程中在运行的托管代码,只能卸载代码运行时所在的域而不能卸载单独的程序集或类型,Unload方法会正常关闭指定的域。这时,载入域的所有程序集都会被移除,并且无法再使用。不过,如果域中的程序集对域是非特定的(域无关程序集,也即共享程序集),则程序集的数据还会保留在内存中,直至整个进程关闭。除了关闭整个进程,没有机制可以卸载这类程序集。由于一个进程中允许包含多个域,某个域可以在不停止整个进程的情况下卸载。以这样的方式卸载不再需要的代码,可以减少内存占用并极大提高应用程序的可缩放性。此外,由于线程并不与域一一对应,当域中存在活动线程时,调用AppDomain.Unload方法可能无法将域卸载并导致异常。在域中加载程序集从上面的论述不难看出:要运行应用程序,必须首先将程序集(.Net下经编译产生,包含IL中间语言、元数据及清单等)加载到域中。而且一个域中可装载多个程序集。默认情况下,公共语言运行库自动将一个程序集加载到包含引用该程序集的代码的域。通过此方法,该程序集的代码和数据独立于使用该程序集的应用程序。自行创建域的好处之一便是可以指定如何装载程序集。在域中有以下两种方式加载程序集:1、将当前程序集加载入单独的域中,同一个程序集可能有多个副本;2、以非特定于域的形式加载程序集,让一个程序集在多个域间共享;这两种方式各自偏重于安全性和性能,需要视具体情况在二者之间权衡。具体地,在 .Net 框架中,System.Reflection.Assembly 类提供以下静态方法将程序集加载至域:· Load()在给顶程序集名称的前提下,加载该程序集: Assembly SampleAssembly;…SampleAssembly = Assembly.Load("System.Data");//根据类型加载程序集 · LoadFrom()在已知程序集文件名或路径等信息的情况下加载程序集: Assembly SampleAssembly;…SampleAssembly = Assembly.LoadFrom("c://Sample.Assembly.dll");//根据已有程序集名称加载概念:AppDomain (应用程序域) 是.NET框架独有的概念。找不到其他技术体系中贴切的参照概念,是微软自己的东西。AppDomain,它是一个应用程序在其中执行的独立环境,为执行托管代码提供隔离、卸载和安全边界。AppDomain的独立性:AppDomain是既是容器又是边界线。.NET运行时间使用AppDomain作为代码和数据的容器,就像操作系统一个过程作为代码和数据的容器一样。当操作系统使用一个过程来分离不整齐的代码时,.NET运行时间使用一个AppDomain来分离在一个安全边线内的代码。每一个AppDomain都由.NET的框架分配了专用的存储区(应用域局部存储)。任何对象都可以访问自己当前所在的AppDomain的局部存储区,这个局部存储区被整个AppDomain中的对象共享,也包括进入AppDomain的线程(运行于同一个AppDomain的线程可以通过这个局部存储进行通信)。进程和AppDomain的关系:属于一对多的关系,即一个进程中可以有多个AppDomain,但是AppDomain只能存在于某个进程中。缺省情况下,如果你没有自己创建多个AppDomain,一个进程启动后自动创建一个AppDomain。多个AppDomain运行在同一个.NET的进程中,好处就是可以降低系统消耗,而且不同的AppDomain之间互相隔离,具有更好的独立性和安全性。线程和AppDomain的关系:在应用程序域和线程之间没有一对一的关联。多个线程可以属于一个应用程序域,尽管给定的线程并不局限于一个应用程序域,但在任何给定时间,线程都在一个应用程序域中执行。线程执行可以涉及多个AppDomain,但某个特定时刻,线程仅存在于一个AppDomain中,且线程可以进入其他的AppDomain。某个程序集的某个实例属于具体的AppDomain,由AppDomain在自己的范围内加载,并按照程序集创建相应的对象。AppDomain是程序集的执行环境,同时程序集作为静态实体,可以被多个AppDomain加载执行。AppDomain的创建和卸载:应用程序域通过使用CreateDomain方法来创建。AppDomain实例用于加载和执行程序集 (Assembly)。当不再使用AppDomain时,可以将它卸载。AppDomainSetup setup = new AppDomainSetup ();setup.LoaderOptimization = LoaderOptimization.SingleDomain;setup.ApplicationBase = "(some directory)";setup.ConfigurationFile = "(some file)";AppDomain domain = AppDomain.CreateDomain ( "newDomain", null, setup );domain.ExecuteAssembly ( "C://NewDomain.exe" );AppDomain.Unload ( domain );那些情况下需要使用AppDomain:其实在一般情况下我们是不需要理会appDomain的,不过,出现此概念在.NET中决非多余,有着自己存在的理由,那么具体载那些情境下要使用appDomain呢?需要隔离的程序集,譬如一些特别容易引起崩溃的代码可以考虑单独运行于一个特定的appDomain不同安全级别的程序集,如果需要为自己的代码划分安全执行的边界,可以考虑将不同安全级别的代码单独创建于某个设定了不同安全信息的appDomain从性能上考虑,有些程序集可能会消耗大量资源,尽管在托管环境下,基本上不存在资源消耗漏洞,但是总会存在特定时间访问密集造成消耗大量资源的情况,这时可以考虑创建单独的appDomain,在资源消耗超过临界点后进行appDomain的卸载,适应系统运行要求。Asp.net中利用不同得appDomain来提供支持就是为了防止一个应用程序的崩溃影响其他asp.net应用程序,同时,在不重新启动的系统不重新启动IIS不影响asp.net自身服务提供的情况下将一个appDomain卸掉同时启动新的appDomain,理想情况下可以实现web系统的长时间在线(这以往是昂贵的unix的特性,终于被MS“借鉴”了)。不同版本的同一应用程序集的同时运行。这个在COM时代是一个大问题,现在通过appDomain,实现了在一个进程中执行版本不同的两个程序集,可以做到良好的兼容性。动态加载一些程序。例如动态创建一个AppDomain启动一个后台监控程序,即使崩溃了也不会影响主进程的运行。改变AppDomain的属性: 使用AppDomain类的CurrentDomain属性,可以检查关于代码正在运行的AppDomain的属性。  如何改变web.config/ App.config位置:Web.config/App.config / *.exe.config都默认在应用程序集的相同目录下,.net 2.0configuration非常依赖这个配置文件,如果我们想把它移到其他目录,比如”C:"Documentsand Settings"myusername"Application Data"”下,而又不影响.net2.0 configuration等等所有依赖这个配置文件的等你哦关系,怎么做呢?可以设置AppDomain的属性:string newConfigFilePath = @"C:/Documents and Settings/myusername/Application Data/MyApp/MyApp.config";if(System.AppDomain.CurrentDomain.SetupInformation.ConfigurationFile != newConfigFilePath)                    AppDomain.CurrentDomain.SetData ( "APP_CONFIG_FILE", newConfigFilePath );

设置AppDomain数据的示例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace thread
{
    
    
class MyAppDomain
    
{
        
public AppDomain Domain;
        
public int ThreadId;
        
public void SetDomainData(string vName, string vValue)
        
{
            Domain.SetData(vName, (
object)vValue); 
            ThreadId
=System.Threading.Thread.CurrentThread.ManagedThreadId;
        }

        
public string GetDomainData(string name)
        
{
            
return (string)Domain.GetData(name);
        }

        
static void Main(string[] args)
        
{
            
string DataName = "MyData";
            
string DataValue = "Some Data to be stored";

            Console.WriteLine(
"Retrieving current domain");
            MyAppDomain Obj 
= new MyAppDomain();
            Obj.Domain 
= AppDomain.CurrentDomain;

            Console.WriteLine(
"Setting domain data");
            Obj.SetDomainData(DataName, DataValue);

            Console.WriteLine(
"Getting domain data");
            Console.WriteLine(
"The Data found for key'" + DataName
                
+ "'is'" + Obj.GetDomainData(DataName) + "'running on thread id:" + Obj.ThreadId);
        }

    }

}

原创粉丝点击