`

浅析.Net下的AppDomain编程

阅读更多
作者:宋华 发文时间:2002
我们知道,进程是操作系统用于隔离众多正在运行的应用程序的机制。在.Net之前,每一个应用程序被加载到单独的进程中,并为该进程指定私有的虚拟内存。进程不能直接访问物理内存,操作系统通过其它的处理把这些虚拟内存映射到物理内存或IO设备的某个区域,而这些物理内存之间不会有重叠,这就决定了一个进程不可能访问分配给另一个进程的内存。相应地,运行在该进程中的应用程序也不可能写入另一个应用程序的内存,这确保了任何执行出错的代码不会损害其地址空间以外的应用程序。在这种机制下,进程作为应用程序之间一个独立而安全的边界在很大程度上提高了运行安全。
进程的缺点是降低了性能。许多一起工作的进程需要相互通信,而进程却不能共享任何内存,你不能通过任何有意义的方式使用从一个进程传递到另一个进程的内存指针。此外,你不能在两个进程间进行直接调用。你必须代之以使用代理,它提供一定程度的间接性。虽然,使用动态连接库dll让所有的组件运行在同一空间,一定程度上可以提高性能,但这些组件相互影响,一个组件的错误将极有可能导致整个应用程序的崩溃,“dll地狱”更是让许多应用程序难以避免。

应用程序域(AppDomain)

在.Net中,应用程序有了一个新的边界:应用程序域(以下简称域)。它是一个用于隔离应用程序的虚拟边界。为了禁止不应交互的代码进行交互,这种隔离是必要的。.Net的应用程序在域层次上进行隔离,一个域中的应用程序不能直接访问另一个域中的代码和数据。这种隔离使得在一个应用程序范围内创建的所有对象都在一个域内创建,确保在同一进程中一个域内运行的代码不会影响其他域内的应用程序,大大提高了运行的安全。

.Net结构中,由于公共语言运行库能够验证代码是否为类型安全的代码,所以它可以提供与进程边界一样大的隔离级别,其性能开销也要低得多。你可以在单个进程中运行几个域,而不会造成进程间调用或切换等方面的额外开销。这种方法是把任何一个进程分解到多个域中,允许多个应用程序在同一进程中运行,每个域大致对应一个应用程序,运行的每个线程都在一个特殊的域中。如果不同的可执行文件都运行在同一个进程空间中,它们就能轻松地共享数据或直接访问彼此的数据。这种代码同运行同一个进程但域不同的类型安全代码一起运行时是安全的。在一个进程内运行多个应用程序的能力显著增强了服务器的可伸缩性。

域间通信

域是.Net 带来的一个重要改进,它不仅将众多在运行的应用程序隔离开来,还不影响彼此间通信。虽然,公共语言运行库禁止在不同域中的对象之间进行直接调用,但我们可以复制这些对象,或通过代理访问这些对象。如果以前一种方式,那么对该对象的调用为本地调用。也就是说,调用方和被引用的对象位于同一域中。如果通过代理访问对象,调用方和被引用的对象位于不同的域中,对该对象的调用被视为远程调用,这种情形与两个进程间的调用或两台计算机间的调用结构大致相同。这时,需要被引用对象的元数据对于两个域均可用,以便.Net即时编译JIT能正确执行。

域与线程的关系

在.Net中,线程是公共语言运行库用来执行代码的操作系统构造。在运行时,所有托管代码均加载到一个域中,由特定的操作系统线程来运行。然而,域和线程之间并不具有一一对应关系。在任意给定时间,单个域中可以执行不止一个线程,而且特定线程也并不局限在单个域内。也就是说,线程可以跨越域边界,不为每个域创建新线程。当然,在指定时刻,每一线程都只能在一个域中执行。运行库会跟踪所有域中有哪些线程正在运行。通过调用.Net类库的 Thread.GetDomain 方法,你还可以确定正在执行的线程所在的域。

域的创建

作为公共语言运行库的隔离单元,域在进程中创建和运行。.Net结构中,运行时宿主(也叫作运行时主机)是负责将运行时载入进程并在域中执行用户代码和托管代码的应用程序。运行时宿主包括ASP.Net、浏览器Internet Explorer 和 Windows等外壳程序,负责创建进程和默认域,例如,Asp.Net为每个运行在web服务器上的web应用程序创建一个域。浏览器Internet explore创建运行受管制控件的域。

对多数应用程序,你并不必须创建相应的域,每次CLR在初始化一个进程时,将创建默认域,并使该进程运行于这个默认域下。然而,默认域不能由任何系统调用来卸载,该域只有在进程被卸载之后才能被销毁。如果直接在默认域下编程或运行代码,而由于某种原因域的代码崩溃了,那么就有使得整个服务随之崩溃的风险。

于是,针对不同的应用程序,应该创建和配置相应的域并载入适当的程序集。.Net为此提供了丰富的类库。其中,AppDomain 类是域的编程接口,其大量的(重载)方法能完成以下任务:

· 创建域

· 在域中加载程序集和类型

· 枚举域中的程序集和线程

· 卸载域

创建新域时,使用AppDomain 类的静态方法CreateDomain。你可以为域命名并按该名称来引用域。下面的示例语句创建新域,并为它指定名称 MyDomain:

<ccid_nobr>
<ccid_code>AppDomain myDomain = AppDomain.CreateDomain("MyDomain");</ccid_code>
</ccid_nobr>

然后你可以查询当前域的名称和新创建子域的名称:

<ccid_nobr>
<ccid_code>string hostDomain=AppDomain.CurrentDomain.FriendlyName;
          string childDomain=myDomain.FriendlyName;</ccid_code>
</ccid_nobr>

在这里,属性FriendlyName表示的是域的友好名称,友好名称通过从程序集的基本代码中去除目录路径而形成。例如,文件名为 "d:\MyAppDomain\MyAssembly.exe" 的程序集加载到默认域中,域的友好名称就是 "MyAssembly.exe"。

更一般的是,在创建域之前,先设置好域的参数,这可以通过类AppDomainSetup来完成。该类的ApplicationBase 属性定义应用程序的根目录, AppDomainSetup 类还有一个极重要的属性变量LoaderOptimizzation,取值可以是MultiDomain,MultiDomainHost和SignleDomain等,用以指定被加载程序集的类别(共享程序集或域专用程序集),例如,以下语句把程序集设置为域专用程序集:

<ccid_nobr>
<ccid_code>appDomainSetup.LoaderOptimization=LoaderOptimizatiion.SigleDomain;</ccid_code>
</ccid_nobr>

对以上两个方面简单归纳一下,对域的典型操作就包括:设置参数然后创建两个步骤,语句示例如下:

<ccid_nobr>
<ccid_code>AppDomainSetup appDomainSetup=new AppDomainSetup();//实例化域设置
appDomainSetup.LoaderOptimization=LoaderOptimization.SingleDomain; //指定域类别
AppDoman ad=AppDomain.CreateDomain(domainName,appDomainSetup); //创建域
...
//应用程序在这里运行代码
...
AppDomain.Unload(ad);//卸载域</ccid_code>
</ccid_nobr>

卸载域

当使用完域时,可使用AppDomain类Unload()静态方法将其卸载。要卸载进程中在运行的托管代码,只能卸载代码运行时所在的域而不能卸载单独的程序集或类型,Unload方法会正常关闭指定的域。这时,载入域的所有程序集都会被移除,并且无法再使用。不过,如果域中的程序集对域是非特定的(域无关程序集,也即共享程序集),则程序集的数据还会保留在内存中,直至整个进程关闭。除了关闭整个进程,没有机制可以卸载这类程序集。由于一个进程中允许包含多个域,某个域可以在不停止整个进程的情况下卸载。以这样的方式卸载不再需要的代码,可以减少内存占用并极大提高应用程序的可缩放性。此外,由于线程并不与域一一对应,当域中存在活动线程时,调用AppDomain.Unload方法可能无法将域卸载并导致异常。

在域中加载程序集

从上面的论述不难看出:要运行应用程序,必须首先将程序集(.Net下经编译产生,包含IL中间语言、元数据及清单等)加载到域中。而且一个域中可装载多个程序集。默认情况下,公共语言运行库自动将一个程序集加载到包含引用该程序集的代码的域。通过此方法,该程序集的代码和数据独立于使用该程序集的应用程序。

自行创建域的好处之一便是可以指定如何装载程序集。在域中有以下两种方式加载程序集:

1、将当前程序集加载入单独的域中,同一个程序集可能有多个副本;

2、以非特定于域的形式加载程序集,让一个程序集在多个域间共享;

这两种方式各自偏重于安全性和性能,需要视具体情况在二者之间权衡。具体地,在 .Net 框架中,System.Reflection.Assembly 类提供以下静态方法将程序集加载至域:

· Load()在给顶程序集名称的前提下,加载该程序集:

<ccid_nobr>
<ccid_code>Assembly SampleAssembly;
…
SampleAssembly = Assembly.Load("System.Data");//根据类型加载程序集</ccid_code>
</ccid_nobr>

· LoadFrom()在已知程序集文件名或路径等信息的情况下加载程序集:

<ccid_nobr>
<ccid_code>Assembly SampleAssembly;
…
SampleAssembly = Assembly.LoadFrom("c:\\Sample.Assembly.dll");//根据已有程序集名称加载</ccid_code>
</ccid_nobr>

参考资料:

《Microsoft .NET Framework程序设计》《.NET Framework高级编程》《.NET框架精髓 》等
分享到:
评论

相关推荐

    AppDomain编程

    ### AppDomain编程详解 #### 一、引言 在.NET框架中,应用程序的隔离与安全性是通过AppDomain(应用程序域)实现的。本篇文章旨在深入探讨AppDomain的概念、原理及其在.NET开发中的应用技巧,适合中高级.NET开发...

    vb.net remoting编程

    VB.NET Remoting是.NET框架中的一种核心技术,它允许在不同应用程序域(AppDomain)之间进行对象间的通信,甚至是跨越网络的通信。这个技术为分布式应用提供了强大的基础,使得开发者能够构建可扩展、高性能且安全的...

    NET分布式编程(C#).pdf

    1. **.NET Remoting**:这是.NET早期的分布式通信机制,允许对象跨应用程序域(AppDomain)或进程边界进行通信。Remoting支持多种通信协议和序列化方式,为开发者提供了高度的灵活性。 2. **WCF**:作为.NET ...

    AppDomain动态加载范例

    在.NET框架中,`AppDomain`是应用程序域的抽象,它是.NET执行环境中的一个核心概念。每个`AppDomain`都是一个独立的沙箱,用于执行代码、管理资源和隔离应用程序。`AppDomain`允许我们创建多租户环境,提供安全性和...

    Visual C# .NET 编程经典——从VB6到Visual C# .NET 快速进阶

    《Visual C# .NET 编程经典——从VB6到Visual C# .NET 快速进阶》这本书是为那些已经熟悉Visual Basic 6 (VB6)并希望过渡到使用Visual C# .NET进行开发的程序员量身打造的。本书旨在帮助读者掌握C#语言的基础和高级...

    【ASP.NET编程知识】.NET Core 3.0 可回收程序集加载上下文的实现.docx

    在.NET Framework 中,AppDomain 被用来加载程序集,但无法单独卸载一个程序集,必须卸载整个AppDomain,这可能导致应用程序中断。而在.NET Core 中,从一开始就没有AppDomain的概念,而是采用AssemblyLoadContext来...

    VB.NET

    VB.NET还引入了泛型,这是一种强大的类型安全机制,可以在不指定具体数据类型的情况下编写代码。泛型集合类(如List)提高了代码的效率和可读性,因为它们在运行时自动适应所需的数据类型。 .NET Framework为VB.NET...

    ASP.NET运行原理.doc

    在深入了解ASP.NET的运行原理之前,我们先明确一下几个关键概念:IIS(Internet Information Services)、ISAPI(Internet Server Application Programming Interface)、APPDOMAIN以及HttpRuntime。 **IIS**:IIS...

    .NET 动态加载DLL文件

    .NET框架中的动态加载DLL文件是一项重要的编程技巧,它允许程序在运行时根据需要加载和使用外部库,从而提高代码的灵活性和模块化。这主要通过使用`System.Reflection`命名空间中的类来实现,特别是`Assembly`类。让...

    新建AppDomain中捕获异常

    在.NET框架中,AppDomain是应用程序域的抽象,它是运行时环境执行代码的基本单元。AppDomain提供了一种隔离机制,使得不同的应用程序可以在同一进程内独立运行,互不影响。这种隔离可以确保一个应用程序的问题不会...

    C# Web服务高级编程——使用.NET Remoting和ASP.NET创建Web服务

    在.NET框架中,C#是一种强大的编程语言,用于构建各种应用程序,包括Web服务。Web服务是一种基于互联网协议的软件,允许不同系统之间进行交互和数据交换。本资源着重讲解了如何利用.NET Remoting和ASP.NET技术来创建...

    asp.net生命周期

    应用程序启动时,ASP.NET会创建一个名为ApplicationManager的类的实例,它负责创建一个应用程序域(AppDomain)。应用程序域是.NET Framework中的一种隔离机制,确保不同应用程序之间的运行时环境独立,避免相互干扰...

    【ASP.NET编程知识】在.net core中实现字段和属性注入的示例代码.docx

    【ASP.NET编程知识】在.NET Core中实现字段和属性注入是一种常见的依赖注入(Dependency Injection,简称DI)方式,它能够帮助开发者创建松耦合、可测试且易于维护的代码。在.NET Core中,内置的依赖注入容器...

    VB.NET Remoting实例

    AppDomain是.NET运行时的一个安全边界,每个应用程序都运行在一个或多个这样的域中。这提供了沙箱环境,使得一个应用程序的故障不会影响到其他应用程序,同时也支持类型隔离和版本控制。 VB.NET Remoting的工作流程...

    ASP.NET的工作原理探索

    本文将深入探讨ASP.NET的工作原理,涵盖其核心组件如APPDOMAIN、ASP.NET生命周期、HTTPAPPLICATION以及IIS的角色。 首先,APPDOMAIN(应用程序域)是.NET Framework中的一个基本概念,它提供了一个安全的执行环境,...

    使用.NET Remoting 创建五子棋对战游戏

    .NET Remoting是微软.NET框架提供的一种机制,用于在不同的应用程序域(AppDomain)之间进行通信,这包括跨进程甚至跨网络的通信。本项目利用.NET Remoting技术来创建一个五子棋对战游戏,让我们深入探讨一下这个...

    VB.NET Remoting 技术手册

    VB.NET Remoting是.NET Framework提供的一种强大的分布式计算技术,它允许对象在不同的应用程序域(AppDomain)之间进行通信,甚至可以跨越网络。本手册将深入探讨这一技术,帮助开发者理解和应用VB.NET Remoting来...

    判断是否.net框架平台含代码

    3. **启动代码**:在程序入口点(如C#中的`Program.cs`的`Main`方法)查找启动代码,看看是否使用了.NET特有的启动机制,如`AppDomain.CurrentDomain.SetupInformation`或`Application.Run`。 4. **编译指令**:...

    Asp.Net知识库

    9. **系统控制**:Asp.Net可以通过System.Diagnostics命名空间进行系统级的控制,如进程管理和调试,以及使用AppDomain进行应用程序域的管理和隔离。 10. **图像和多媒体**:Asp.Net支持处理图像和多媒体内容,可以...

Global site tag (gtag.js) - Google Analytics