一、概述

使用.NET建立的可执行程序 *.exe,并没有直接承载到进程当中,而是承载到应用程序域(AppDomain)当中。在一个进程中可以包含多个应用程序域,一个应用程序域可以装载一个可执行程序(*.exe)或者多个程序集(*.dll),这样可以使应用程序域之间实现深度隔离,即使进程中的某个应用程序域出现错误,也不会影响其他应用程序域的正常运作。处理asp.net所涉及的类大多数定义在System.Web程序集中。

  1. 当exe程序集加载完毕,.Net会在当前进程中创建一个默认应用程序域,这个应用程序域的名称与程序集名称相同。默认应用程序域不能被卸载,并且与其所在的进程同生共灭。
  2. 应用程序域只是允许它所加载的程序集访问由.Net Runtime所提供的服务。这些服务包括托管堆(Managed Heap),垃圾回收器(Garbage collector),JIT 编译器等.Net底层机制.
  3. 一个应用程序域中如果出现了致命错误导致崩溃,只会影响其本身,而不会影响到其他应用程序域。实现了错误隔离。

应用程序域是进程中承载程序集的逻辑分区,在应用程序域中存在更细粒度的用于承载.NET对象的实体,即.NET上下文Context。

所有的.NET对象都存在于.NET上下文当中,每个AppDomain当中至少存在一个默认上下文(context0),:

优点

托管程序为什么要使用应用程序域呢?概括其优点如下:

  1. 在一个应用程序中出现的错误不会影响其他应用程序。
  2. 因为类型安全的代码不会导致内存错误,所以使用应用程序域可以确保在一个域中运行的代码不会影响进程中的其他应用程序。
  3. 能够在不停止整个进程的情况下停止单个应用程序。
  4. 使用应用程序域时,可以卸载在单个应用程序中运行的代码。
  5. 在一个应用程序中运行的代码不能直接访问其他应用程序中的代码或资源。

属性

  • ActivationContext           获取当前应用程序域的激活上下文。
  • ApplicationIdentity           获得应用程序域中的应用程序标识。
  • BaseDirectory  获取基目录,它由程序集冲突解决程序用来探测程序集。
  • CurrentDomain  获取当前 Thread 的当前应用程序域。
  • FriendlyName              获取此应用程序域的友好名称。
  • Id  获得一个整数,该整数唯一标识进程中的应用程序域。
  • SetupInformation     获取此实例的应用程序域配置信息。

方法

  • CreateDomain(String)  使用指定的名称新建应用程序域。
  • CreateInstance(String, String)   创建在指定程序集中定义的指定类型的新实例。
  • CreateInstanceAndUnwrap(String, String) 创建指定类型的新实例。 形参指定定义类型的程序集以及类型的名称。
  • CreateInstanceFrom(String, String)   创建在指定程序集文件中定义的指定类型的新实例。
  • DoCallBack           在另一个应用程序域中执行代码,该应用程序域由指定的委托标识。
  • ExecuteAssembly(String)   执行指定文件中包含的程序集。
  • ExecuteAssemblyByName(String)     在给定其显示名称的情况下执行程序集。
  • GetAssemblies      获取已加载到此应用程序域的执行上下文中的程序集。
  • GetData         为指定名称获取存储在当前应用程序域中的值。
  • IsDefaultAppDomain   返回一个值,指示应用程序域是否是进程的默认应用程序域。
  • Load(AssemblyName)  在给定 AssemblyName 的情况下加载 Assembly。
  • SetData(String, Object)  为指定的应用程序域属性分配指定值。
  • Unload           卸载指定的应用程序域。

程序集

程序集是包含一个或多个类型定义文件和资源文件的集合。它允许我们分离可重用类型的逻辑表示和物理表示。程序集可以是静态的或动态的。静态程序集可以包括 .NET Framework 类型(接口和类),以及该程序集的资源(位图、JPEG 文件、资源文件等)。静态程序集存储在磁盘上的可移植可执行 (PE) 文件中。您还可以使用 .NET Framework 来创建动态程序集,动态程序集直接从内存运行并且在执行前不存储到磁盘上。您可以在执行动态程序集后将它们保存在磁盘上。

全局程序集缓存:安装有公共语言运行时的每台计算机都具有称为全局程序集缓存的计算机范围内的代码缓存。 全局程序集缓存中存储了专门指定给由计算机中若干应用程序共享的程序集。

二、基本操作

在.Net 中,将应用程序域封装为了AppDomain类,这个类提供了应用程序域的各种操作,包含 加载程序集、创建对象、创建应用程序域 等。通常的编程情况下下,我们几乎从不需要对AppDomain进行操作。

1、获取当前运行的代码所在的应用程序域。

可以使用AppDomain类的静态属性CurrentDoamin,获取当前代码所在的应用程序域;或者使用Thread类的静态方法GetDomain(),得到当前线程所在的应用程序域:

AppDomain currentDomain = AppDomain.CurrentDomain;
AppDomain currentDomain = Thread.GetDomain();

NOTE:一个线程可以访问进程中所包含的所有应用程序域,因为虽然应用程序域是彼此隔离的,但是它们共享一个托管堆(Managed Heap)。

2、获取应用程序域的名称。

使用AppDomain的实例只读属性,FriendlyName:

string name = AppDomain.CurrentDomain.FriendlyName;

3、从当前应用程序域中创建新应用程序域。

可以使用CreateDomain()静态方法,并传入一个字符串,作为新应用程序域的名称(亦即设置FriendlyName属性):

AppDomain newDomain = AppDomain.CreateDomain("New Domain");

4、配置应用程序域(在创建前):

AppDomainSetup domainInfo = new AppDomainSetup();
domainInfo.ApplicationBase = "e:\\AttributeTest";
domainInfo.ConfigurationFile = "e:\\AttributeTest\\1.exe.config";
AppDomain domain = AppDomain.CreateDomain("MyDomain", null, domainInfo);

5、判断是否为默认应用程序域:

newDomain.IsDefaultAppDomain()

6、卸载应用程序集

AppDomain.Unload(newDomain);

三、在新的应用程序域中执行程序集

加载可执行程序:

AppDomain newAppDomain = AppDomain.CreateDomain("newAppDomain");
newAppDomain.ExecuteAssembly("Example.exe");//或使用ExecuteAssemblyByName
AppDomain.Unload(newAppDomain);

四、在新的应用程序域中创建对象

将程序集加载到应用程序域中的方式:

  • a.Assembly.Load(assemblyName);使用程序集名来加载到应用程序域中,为常用方法。
  • b.Assembly.LoadFrom():加载给定其文件位置的程序集,通过此方法加载程序集将使用不同的加载上下文。
  • c.Assembly.ReflectionOnlyLoad和 Assembly.ReflectionOnlyLoadFrom:将程序集加载到仅反射上下文中,可检查(但不能执行)加载到该上下文中的程序集,同时允许检查以其他平台为目标的程序集。
  • e.Type.GetType()加载程序集
  • f.AppDomain的Load方法加载程序集。主要用于实现COM互操作性,不应该使用该方法将程序集加载到除从其调用该方法的应用程序域以外的其他应用程序域。
  • d.AppDomain的CreateInstance和CreateInstanceAndUnwrap方法。

具体见反射基础:https://www.cnblogs.com/springsnow/p/9433924.html

在新的应用程序域中创建对象,可以使用AppDomain的实例方法CreateInstanceAndUnWrap()或者CreateInstance()方法。方法包含两个参数,第一个参数为类型所在的程序集,第二个参数为类型全称(这两个方法后面会详述):

DemoClass obj = (DemoClass)newDomain.CreateInstanceAndUnWrap("ClassLib", "ClassLib.DemoClass");
//或者
ObjectHandle objHandle = newDomain.CreateInstance("ClassLib", "ClassLib.DemoClass");
DemoClass obj = (DemoClass)objHandle.UnWrap();
//或者
DemoClass obj = (DemoClass)newDomain.CreateInstanceFromAndUnWrap("c:\\ClassLib.dll", "ClassLib.DemoClass");

注意:obj是在当前的默认应用程序域声明的;而类型的实例(对象本身)却在新创建的应用程序域NewDomain中创建的。这样就出现了一种尴尬的情况:而默认情况下AppDomain是彼此隔离的,我们不能直接在一个应用程序中引用另一个应用程序域中的对象,所以这里便会引发异常。

跨AppDomain运行代码:

void Main()
{
//建立新的应用程序域对象
AppDomain newAppDomain = AppDomain.CreateDomain("newAppDomain");
CrossAppDomainDelegate crossAppDomainDelegate = new CrossAppDomainDelegate(MyCallBack); newAppDomain.DoCallBack(crossAppDomainDelegate); newAppDomain.DomainUnload += (obj, e) =>
{
Console.WriteLine("New Domain unload!");
}; AppDomain.Unload(newAppDomain);
}
static public void MyCallBack()
{
string name = AppDomain.CurrentDomain.FriendlyName;
for (int n = 0; n < 4; n++)
Console.WriteLine(string.Format(" Do work in {0}........", name));
}

五、封值传送,Serializable

我们可以将对象标记为可序列化,将对象本身由另一应用程序域(远程)传递到本地应用程序域中。

原理: 先在远程创建对象,接着将对象序列化,然后传递对象,在本地进行反序列化,最后还原对象。

当位于ConsoleApp.exe的obj引用NewDomain中创建的对象时,.Net将NewDomain中对象的状态进行复制、序列化,然后在ConsoleApp.exe中重新创建对象,还原状态,并通过代理来对对象进行访问。这种跨应用程序域的访问方式叫做 传值封送(Marshal by value)

[Serializable]
public class DemoClass { /*略*/ }

六、传引用封送,MarshalByRefObject

让对象依然保留在远程(本例为NewDomain中),而在客户端仅创建代理,上面已经说了代理的接口和远程对象完全相同,所以客户端以为仍然访问的是远程对象,当客户端调用代理上的方法时,由代理将对方法的请求发送给远程对象,远程对象执行方法请求,最后再将结果传回。这种方式叫做 传引用封送(Marshal by reference)

对象或者对象引用在传递的过程中,是以一种包装过的状态(warpper state)进行传递。所以在创建对象时,要解包装,因此在CreateInstanceAndUnWrap()方法后多了一个AndUnWrap后缀,实际上UnWrap还包含一个创建代理的过程。

让对象继承自MarshalByRefObject基类,实现传引用封送。

public class DemoClass:MarshalByRefObject {/*略*/}

上下文绑定对象:ContextBoundObject

当系统需要对象使用消息接收器机制的时候,即可使用ContextBoundObject类。ContextBoundObject继承了MarshalByRefObject类,保证了它的子类都会通过透明代理被访问。

一般类所建立的对象为上下文灵活对象(context-agile),它们都由CLR自动管理,可存在于任意的上下文当中。而 ContextBoundObject 的子类所建立的对象(称作上下文绑定对象)只能在建立它的对应上下文中正常运行,此状态被称为上下文绑定。其他对象想要访问ContextBoundObject 的子类对象时,都只能通过代透明理来操作。ContextBound还有一个Synchronization特性,此特性会保证ContextBound对象被加载到一个线程安全的上下文当中运行。

七、应用程序域、进程与线程关系。

概述:

  1. 进程之间彼此是完全隔绝的,而线程与运行在相同进程的其他线程共享堆heap内存。
  2. 在应用程序域和线程之间没有一对一的关联。

进程(Process)、线程(Thread)、应用程序域(AppDomain)、上下文(Context)的关系如图5.0。

一个进程内可以包括多个应用程序域,也有包括多个线程,线程也可以穿梭于多个应用程序域当中。但在同一个时刻,线程只会处于一个应用程序域内。线程也能穿梭于多个上下文当中,进行对象的调用。

应用程序域 System.AppDomain,动态加载程序集的更多相关文章

  1. C# 动态加载程序集信息

    本文通过一个简单的实例,来讲解动态加载Dll需要的知识点.仅供学习分享使用,如有不足之处,还请指正. 在设计模式的策略模式中,需要动态加载程序集信息. 涉及知识点: AssemblyName类,完整描 ...

  2. C# 动态加载程序集dll (实现接口)

    一.程序集(接口程序集):LyhInterface.Dll namespace LyhInterface { public interface ILyhInterface { void Run(); ...

  3. C#动态加载程序集(转)

    C#动态加载程序集 今天在看网络上的一篇关于‘.NET应用自动部署窗体技术’.NET的自动部署技术构造在.NET框架之中,它使得应用程序能够通过HTTP连接从远程服 务器按需下载程序集.有了这个功能, ...

  4. .Net Core 通过依赖注入和动态加载程序集实现宿程序和接口实现类库完全解构

    网上很多.Net Core依赖注入的例子代码,例如再宿主程序中要这样写: services.AddTransient<Interface1, Class1>(); 其中Interface1 ...

  5. Assembly.Load动态加载程序集而不占用文件 z

    方式一:占用文件的加载 Assembly assembly = Assembly.Load(path); 用上面的方法可以动态的加载到dll,但是用这种方法加载到的dll一直到程序运行结束都是占用的d ...

  6. C# 反射实现动态加载程序集

    原文:https://blog.csdn.net/pengdayong77/article/details/47622235 在.Net 中,程序集(Assembly)中保存了元数据(MetaData ...

  7. 关于c#动态加载程序集的一些注意事项

    Assembly下有LoadFile,LoadFrom等方法可以加载程序集. LoadFile只加载你给定路径的那个dll,LoadFrom会自动加载依赖的dll. 如:A依赖B,LoadFile(& ...

  8. asp.net动态加载程序集创建指定类的实例及调用指定方法

    以下类中有三个方法: LoadAssembly:加载指定路径的程序集 GetInstance:根据Type动态获取实例,用泛型接到返回的类型 ExecuteMothod:执行实例中的指定方法 /// ...

  9. C# 动态加载程序集

    定义动态程序集 namespace DynamicAssembly { public class CodeDriver : MarshalByRefObject { private string pr ...

随机推荐

  1. Linux_高级用法

    LInux如何压缩和解压文件 文件压缩与解压主要讲zip和tar 安静模式和文件夹 zip -r -q -o test.zip 需要打包文件 查看打包文件 du -h test.zip 上节学过的fi ...

  2. Android模拟器Genymotion安装使用教程详解

    一.注册\登录 打开Genymotion官网,https://www.genymotion.com/ ,首先点击右上角的Sign in进行登录操作.如何登录就不细讲了,下面讲一下如何注册(备注:注册按 ...

  3. Amazon SQS 消息队列服务

    Amazon sqs是亚马逊提供的线上消息队列服务, 可以实现应用程序解耦,以及可靠性保证. sqs提供了两种消息队列, 一种是标准消息队列, 一种是先进先出队列(FIFO), 其区别是FIFO是严格 ...

  4. tp5 关键字模糊查询 日期查询 小于大于某范围等查询的优点

    挺不错,用熟了这tp5封装的很方便. 类似上边一个查询多个操作,基本在model 一个方法搞定代码也不用很多, 首先要学会用scope  网上搜tp scope 有几个例子可以借鉴 model 内添加 ...

  5. 谷歌chrome浏览器提示“喔唷 崩溃啦”的解决方案

    原因分析:有可能是注册列表被一些卫士类优化工具或杀毒软件优化了. 解决方案:1. 卸载谷歌浏览器. ①开始→控制面板→添加或删除程序→找到谷歌浏览器卸载(卸载时勾选删除数据) ② 进入注册列表删除谷歌 ...

  6. Matlab脚本和函数

    脚本和函数 脚本: 特点:按照文件中所输入的指令执行,一段matlab指令集合.运行后,运算过程产生的所有变量保存在基本工作区.可以进行图形输出,如plot()函数. 举例: 脚本文件ex4_15.m ...

  7. 【转载】Response对象的作用以及常用方法属性

    Response对象是Asp.Net应用程序中非常重要的一个内置对象,其作用为负责将服务器执行好的信息输出给客户端,即作用主要为响应客户端请求并将服务器的响应返回给用户,在页面的临时跳转中,也可使用R ...

  8. alt和title的区别

    alt是html标签的属性,而title既是html标签,又是html属性. 在图像标签img中,除了常用的宽度width和高度height属性之外,还有两个比较重要并且也会用到的属性,就是alt和t ...

  9. vue 使用vue-video-player播放hls格式视频

    安装 vue-video-player   在 “ devDependencies ” 中 安装  videojs-contrib-hls 在“ dependencies ”中   main.js 中 ...

  10. kvm虚拟机控制台登录配置

    vm虚拟机能否像xen虚拟机一样通过virsh console 一样采用字符界面进行linux虚拟机控制台呢,答案是肯定的,默认情况下该命令是不起作用的,需要修改相关文件才能实现. 本文出自:http ...