AppDomain 详解

AppDomain是CLR的运行单元,它可以加载Assembly、创建对象以及执行程序。

AppDomain是CLR实现代码隔离的基本机制。
每一个AppDomain可以单独运行、停止;每个AppDomain有自己默认的异常处理;
一个AppDomain的运行失败不会影响到其他的AppDomain。
CLR在被CLR Host(windows shell or InternetExplorer or SQL Server)加载后,要创建一个默认的AppDomain,程序的入口点
(Main方法)就是在这个默认的AppDomain中执行。
1.AppDomain vs 进程
AppDomain被创建在进程中,一个进程内可以有多个AppDomain。一个AppDomain只能属于一个进程。
2.AppDomain vs 线程
其实两者本来没什么好对比的。AppDomain是个静态概念,只是限定了对象的边界;线程是个动态概念,它可以运行在不同的
AppDomain。
一个AppDomain内可以创建多个线程,但是不能限定这些线程只能在本AppDomain内执行代码。
CLR中的System.Threading.Thread对象其实是个soft thread,它并不能被操作系统识别;操作系统能识别的是hard thread。
一个soft thread只属于一个AppDomain,穿越AppDomain的是hard thread。当hard thread访问到某个AppDomain时,一个
AppDomain就会为之产生一个soft thread。
hard thread有thread local storage(TLS),这个存储区被CLR用来存储这个hard thread当前对应的AppDomain引用以及soft
thread引用。当一个hard thread穿越到另外一个AppDomain时,TLS中的这些引用也会改变。
当然这个说法很可能是和CLR的实现相关的。
3.AppDomain vs Assembly
Assembly是.Net程序的基本部署单元,它可以为CLR提供用于识别类型的元数据等等。Assembly不能单独执行,它必须被加载到
AppDomain中,然后由AppDomain创建程序集中的对象。
一个Assembly可以被多个AppDomain加载,一个AppDomain可以加载多个Assembly。
每个AppDomain引用到某个类型的时候需要把相应的assembly在各自的AppDomain中初始化。因此,每个AppDomain会单独保持一
个类的静态变量。
4.AppDomain vs 对象
任何对象只能属于一个AppDomain。AppDomain用来隔离对象,不同AppDomain之间的对象必须通过Proxy(reference type)或者
Clone(value type)通信。
引用类型需要继承System.MarshalByRefObject才能被Marshal/UnMarshal(Proxy)。
值类型需要设置Serializable属性才能被Marshal/UnMarshal(Clone)。
5.AppDomain vs Assembly Code
AppDomain和程序集的源代码是什么关系呢?每个程序集的代码会分别装载到各个AppDomain中?
首先我们要把程序集分3类
1.mscorlib,这是每个.net程序都要引用到的程序集。
2.GAC,这个是强命名的公用程序集,可以被所有的.net程序引用。
3.Assembly not in GAC,这是普通的assembly,可以不是强命名,不放到GAC中。
启动CLR,进入entry point时可以设置LoaderOptimization属性:
[LoaderOptimization(LoaderOptimization.MultiDomain]
static void Main()
{...}
LoaderOptimization属性可以设置三个不同的枚举值,来设置针对前面说的三种程序集的代码存放以及访问方式。


1.SingleDomain,由于只启动一个AppDomain,那么code就被直接装载到了AppDomain中,访问静态变量更快捷。
2.MultiDomain,所有的Assembly代码是进程级别的,因此所有的AppDomain只访问一份代码。这大大减少了程序占用的内存,但
是由于程序集的静态变量仍然在各个AppDomain中,因此代码访问静态变量需要先得到AppDomain的引用再进行转换,速度会受到
影响。
3.MultiDomainHost,只有GAC代码是共享的,非GAC的Assembly依然会加载到被使用的AppDomain中,这样提高了静态变量的访问
速度,当然也增加了程序占用的内存。

不管是哪种方式,mscorlib始终是process级别的,即只有一份mscorlib代码在内存中。

C#中动态加载和卸载DLL

在C++中加载和卸载DLL是一件很容易的事,LoadLibrary和FreeLibrary让你能够轻易的在程序中加载DLL,然后在任何地方 卸载。在C#中我们也能使用Assembly.LoadFile实现动态加载DLL,但是当你试图卸载时,你会很惊讶的发现Assembly没有提供任何 卸载的方法。这是由于托管代码的自动垃圾回收机制会做这件事情,所以C#不提供释放资源的函数,一切由垃圾回收来做。 

这引发了一个问题,用Assembly加载的DLL可能只在程序结束的时候才会被释放,这也意味着在程序运行期间无法更新被加载的DLL。而这个功能在某 些程序设计时是非常必要的,考虑你正在用反射机制写一个查看DLL中所有函数详细信息的程序,程序提供一个菜单让用户可以选择DLL文件,这时就需要让程 序能够卸载DLL,否则一旦用户重新得到新版本DLL时,必须要重新启动程序,重新选择加载DLL文件,这样的设计是用户无法忍受的。 

C#也提供了实现动态卸载DLL的方法,通过AppDomain来实现。AppDomain是一个独立执行应用程序的环境,当AppDomain被卸载的 时候,在该环境中的所有资源也将被回收。关于AppDomain的详细资料参考MSDN。下面是使用AppDomain实现动态卸载DLL的代码:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.Threading;
  5. using System.Reflection;
  6. namespace UnloadDll
  7. {
  8. class Program
  9. {
  10. static void Main(string[] args)
  11. {
  12. string callingDomainName = AppDomain.CurrentDomain.FriendlyName;//Thread.GetDomain().FriendlyName;
  13. Console.WriteLine(callingDomainName);
  14. AppDomain ad = AppDomain.CreateDomain("DLL Unload test");
  15. ProxyObject obj = (ProxyObject)ad.CreateInstanceFromAndUnwrap(@"UnloadDll.exe", "UnloadDll.ProxyObject");
  16. obj.LoadAssembly();
  17. obj.Invoke("TestDll.Class1", "Test", "It's a test");
  18. AppDomain.Unload(ad);
  19. obj = null;
  20. Console.ReadLine();
  21. }
  22. }
  23. class ProxyObject : MarshalByRefObject
  24. {
  25. Assembly assembly = null;
  26. string curPath;
  27. public ProxyObject()
  28. {
  29. curPath = System.Environment.CurrentDirectory;
  30. }
  31. public void LoadAssembly()
  32. {
  33. assembly = Assembly.LoadFile(curPath + @"\TestDLL.dll");
  34. }
  35. public bool Invoke(string fullClassName, string methodName, params Object[] args)
  36. {
  37. if (assembly == null)
  38. return false;
  39. Type tp = assembly.GetType(fullClassName);
  40. if (tp == null)
  41. return false;
  42. MethodInfo method = tp.GetMethod(methodName);
  43. if (method == null)
  44. return false;
  45. Object obj = Activator.CreateInstance(tp);
  46. method.Invoke(obj, args);
  47. return true;
  48. }
  49. }
  50. }

TestDLL代码:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. namespace TestDll
  6. {
  7. public class Class1
  8. {
  9. public void Test(string str)
  10. {
  11. Console.WriteLine(str);
  12. }
  13. }
  14. }

注意:

1. 要想让一个对象能够穿过AppDomain边界,必须要继承MarshalByRefObject类,否则无法被其他AppDomain使用。 

2. 每个线程都有一个默认的AppDomain,可以通过Thread.GetDomain()来得到

 
 
 

C# AppDomain 详解的更多相关文章

  1. .Net AppDomain详解(二)

    AppDomain 类 表示应用程序域,它是一个应用程序在其中执行的独立环境. 此类不能被继承. 命名空间:   System程序集:  mscorlib(位于 mscorlib.dll) 继承层次结 ...

  2. AppDomain 详解(转)

    AppDomain是CLR的运行单元,它可以加载Assembly.创建对象以及执行程序. AppDomain是CLR实现代码隔离的基本机制. 每一个AppDomain可以单独运行.停止:每个AppDo ...

  3. .Net AppDomain详解(一)

    AppDomain是CLR的运行单元,它可以加载Assembly.创建对象以及执行程序.AppDomain是CLR实现代码隔离的基本机制. 每一个AppDomain可以单独运行.停止:每个AppDom ...

  4. C#进阶系列——WebApi 接口返回值不困惑:返回值类型详解

    前言:已经有一个月没写点什么了,感觉心里空落落的.今天再来篇干货,想要学习Webapi的园友们速速动起来,跟着博主一起来学习吧.之前分享过一篇 C#进阶系列——WebApi接口传参不再困惑:传参详解  ...

  5. App.Config详解及读写操作

    App.Config详解及读写操作   App.Config详解 应用程序配置文件是标准的 XML 文件,XML 标记和属性是区分大小写的.它是可以按需要更改的,开发人员可以使用配置文件来更改设置,而 ...

  6. 8天掌握EF的Code First开发系列之3 管理数据库创建,填充种子数据以及LINQ操作详解

    本文出自8天掌握EF的Code First开发系列,经过自己的实践整理出来. 本篇目录 管理数据库创建 管理数据库连接 管理数据库初始化 填充种子数据 LINQ to Entities详解 什么是LI ...

  7. log4net详解(转载)

    1.概述 log4net是.Net下一个非常优秀的开源日志记录组件.log4net记录日志的功能非常强大.它可以将日志分不同的等级,以不同的格式,输出到不同的媒介.本文主要是介绍如何在Visual S ...

  8. App.Config详解

    App.Config详解 应用程序配置文件是标准的 XML 文件,XML 标记和属性是区分大小写的.它是可以按需要更改的,开发人员可以使用配置文件来更改设置,而不必重编译应用程序.配置文件的根节点是c ...

  9. c# App.Config详解

    c# App.Config详解 应用程序配置文件是标准的 XML 文件,XML 标记和属性是区分大小写的.它是可以按需要更改的,开发人员可以使用配置文件来更改设置,而不必重编译应用程序. 配置文件的根 ...

随机推荐

  1. C语言:数据类型转换

    #include <stdio.h> main() { printf("%d\n",sizeof(1)); printf("%d\n",sizeof ...

  2. python adb 关闭拼多多

    def gbpdd(sjh): aaka="adb -s {0} shell am force-stop com.xunmeng.pinduoduo".format(sjh) aa ...

  3. 【经典结构】单例模式Singleton

    单例模式Singleton 1.含义 单例模式:即一个类只能创建一个实例. 只有一个实例 --> 不可以从类外new对象 --> 构造器私有化private --> 从类里创建实例: ...

  4. Scrapy 爬虫框架学习笔记(未完,持续更新)

    Scrapy 爬虫框架 Scrapy 是一个用 Python 写的 Crawler Framework .它使用 Twisted 这个异步网络库来处理网络通信. Scrapy 框架的主要架构 根据它官 ...

  5. PostgreSQL数据库结构

    PG数据存储结构分为:逻辑结构和物理存储. 一.逻辑存储结构是:内部的组织和管理数据的方式[逻辑存储结构适用于不同的操作系统和硬件平台] 二.物理存储结构是:操作系统中组织和管理数据的方式. 1.逻辑 ...

  6. 在Python中执行普通除法

    如果希望Python只执行普通的除法,那么可以在程序前加上以下语句: 1 from _future_ import division 如果通过命令行(比如在Linux系统上)运行Python,可以使用 ...

  7. 在 CentOS 7.5 64位上使用 yum 安装 MySQL 8.0

    前段时间在 CentOS 7.5 64位上安装 MySQL 8.0.查了些资料,在这里记录一下详细的安装和设置步骤. 一.安装 使用yum安装MySQL之前需要先下载对应的.rpm文件,下载方法: 去 ...

  8. php-socket通信演示

    client: error_reporting(E_ALL); set_time_limit(0); echo "<h2>TCP/IP Connection</h2> ...

  9. 打开随身U盘_办公专用盘 2019年11月29日

    ;;; ; 打开随身U盘_办公专用盘 2019年11月29日 ; https://www.autoahk.com/?p=16553; https://www.cnblogs.com/delphixx/ ...

  10. noi linux 2.0 体验

    一.起因 下午,我打开 noi 官网准备报名 csp j/s,一看官网展板:"noi linux 2.0 发布" 我就兴奋了起来.(9 月 1 日起开始使用, 也就意味着 csp ...