对于执行代码的作用域和资源所有权的范围,许多编程技术和环境都定义了各自不同的模型。对于操作系统而言,作用域模型是基于进程的;对Java虚拟机而言,作用域模型是基于类装载器的;对于Tnternet信息服务(IIS)和活动服务器页面(ASP)而言,作用域模型是基于虚拟目录的:对于CLR来说,基本的作用域则是AppDomain,这也是本章讨论的重点。
执行范围和CLR
AppDomain(应用程序域),与操作系统的进程扮演相同的角色。像进程一样,AppDomain规定了代码的执行范围,提供了错误隔离的程度,提供了一个安全隔离度,并且拥有自己的资源。一般情况下,你所知道的操作系统进程的大部分情形都可能适用于AppDomain。
AppDomain与进程十分相似,但终究是两个不同的东西。进程是由操作系统创建的,而AppDomain是由CLR创建的。一个给定的AppDomain必须驻留在一个os进程中,而一个给定的进程可以寄宿多个AppDomain图8.1展示了这种关系。
创建AppDomain的开销要低于创建os进程的开销.同样,跨AppDomain边界的开销也比跨操作系统os边界的要低一些.然而,与os进程一样,在AppDomain之间共享数据十分困难(甚至是不可能的)。共享数据难度大的原因之一在于对象与AppDomain的关联。
一个对象正好存放在一个AppDomnin中,值也一样。并且,一个AppDomain中的对象引用必须是引用同-个AppDomain中的对象。在这个方面,AppDomain的行为就好像拥有自己私有的地址空间。然而,这种行为只不过是一种假象而已,因为所有这一切都是由一个无法验证(nonverifiable)的方法操作整个内存实现的,这无疑戳穿了这个假象。如果只有验证过的代码执行,那么,这个假象实际上是一个 规则,无法验证的代码戳穿了CLR AppDomain的假象,这与同样戳穿OS进程假象的内核代码如出一辙。
像对象一样,类型明确地驻留在某个AppDomain中。如果两个AppDomain需要使用一个类型,那么,必须为每个AppDomain分别初始化和分配一次类型。此外,必须为各个用到类型的AppDomain分别加载和初始化一次类型的方法和程序集。田于进程中的各个AppDomain要维护类型的不同拷贝,因此,对于类型的静态字段,每个AppDomain都有其自己的私有副本。图8.2展示了AppDomain、对象和类型之间的关系。
像进程一样,AppDomain是一个所有权(ownership)的单元。AppDomain的资源包括被加载的模块、程序集和类型。只要AppDomain被加载,这些资源就一直在内存中。卸载AppDomain是唯一卸载模块或者程序集的途径。卸载AppDomain也是回收类型静态字段所占内存的唯一方式。
我们在讨论进程时,线程是比较难说清楚的主题。CLR对执行代码模式有着自己的抽象,概念上与线程相似。CLR定义了一个类型System.Threading.Thread。在AppDomain中表示为可调度的(schedulable)实体。System.Threading.Thread线程对象有时被引用为数线程(soff thread)。原因是它的构造无法为底层操作系统所识别。相比之下,OS线程被引用为硬线程(hard thread),因为它们是由OS处理的。
硬线程和CLR软线程对象之间并不存在一对一的关系。然而,不管是基于编程模型,还是根据对现有CLR实现的经验分析,这种关系实际上都是存在的。其一,一个CLR软线程对象驻留在一个确定的AppDomain中。这是AppDomain的工作方式及其含义的副产品(byproduct),并且,无论CLR实现如何变化,都会是这样。其二,一个给定的AppDomain可能有多个软线程对象.在这个当前的实现中 当两个或者两个以上硬线程在单个AppDomain中执行代码时,这种情形就会发生。所有关于硬线程与软线程之间关系的其他假定,都是与实现相关的,那些反例,也有一些值得关注的考察记录。
在CLR的当前实现中,对于给定的AppDomain,硬线程至多有一个软线程对象属于它。并且,如果一个硬线程运行在多个AppDomain中,那么,每个AppDomain都会有一个明显的软线程对象属于该线程。小过,如果硬线程没有进入给定的AppDomain,那么,该AppDomain就不会有软线程表示它。图8.3说明了这种看法。
最后,每次当给定硬线程进入AppDomain后,它就会得到同样的软线程对象。这里需要重申的是:这些看法只是通过观测当前的CLR实现得出的结论。特别是CLR寄宿在弹性不好(fiber-based)的环境时(例如,SQL Server),这些假定中就会有一些不成立的部分。这时,一个硬线程对每个AppDomain而言,可能最多只对应一个软线程。
CLR维护硬线程的线程本地存储区(thread local storage,TLS)中的一些信息。特别是,你会发现硬线程的TLS引用当前AppDomain和软线程对象。当硬线程越过一个AppDomain到另一个AppDomain时,CLR将自动调整这些引用,使其指向新的“当前”AppDomain和软线程。CLR的当前实现维护每AppDomain的线程表,确保给定硬线程只属于每AppDomain的一个软线程对象。
软线程对象具有自已私有的TLS没有什么价值。事实上,你可以通过Thread.GetData和Thread.SetData方法对它进行访问。因为这个TLS与软线程对象进行绑定,当硬线程在先前的AppDomain中执行时,就会保存这个较线程的TLS:一旦越过硬线程AppDomain的边界时,就不能看到它了。|邁tXTb€(u7b
N菓諷>f_剉L?N
N饛臽b_闠*N_N
NO裇u0<