zoukankan      html  css  js  c++  java
  • 转载-AppDomain详解

    转自:https://www.cnblogs.com/foman/archive/2009/10/18/1585655.html

    • 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类
      a. mscorlib,这是每个.net程序都要引用到的程序集。
      b. GAC,这个是强命名的公用程序集,可以被所有的.net程序引用。
      3.Assembly not in GAC,这是普通的assembly,可以不是强命名,不放到GAC中。
      启动CLR,进入entry point时可以设置LoaderOptimization属性:
      [LoaderOptimization(LoaderOptimization.MultiDomain]
      static void Main()
      {...}
      
      

    LoaderOptimization属性可以设置三个不同的枚举值,来设置针对前面说的三种程序集的代码存放以及访问方式。
    LoaderOptimization Enumeration/Attribute

    Value Expected Domains in Process Each Domain Expected to Run ... Code for MSCORLIB Code for Assemblies in GAC Code for Assemblies not in GAC
    SingleDomain One N/A Per-process Per-domain Per-domain
    MultiDomain Many Same Program Per-process Per-process
    MultiDomainHost Many Different Programs Per-process Per-process Per-domain
    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的代码,

        using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Threading;
    using System.Reflection;
    namespace UnloadDll
    {
          class Program
          {
                static void Main(string[] args)
                {
                      string callingDomainName = AppDomain.CurrentDomain.FriendlyName;//Thread.GetDomain().FriendlyName;
                      Console.WriteLine(callingDomainName);
                      AppDomain ad = AppDomain.CreateDomain("DLL Unload test");
                      ProxyObject obj = (ProxyObject)ad.CreateInstanceFromAndUnwrap(@"UnloadDll.exe", "UnloadDll.ProxyObject");
                      obj.LoadAssembly();
                      obj.Invoke("TestDll.Class1", "Test", "It's a test");
                      AppDomain.Unload(ad);
                      obj = null;
                      Console.ReadLine();
                }
          }
          class ProxyObject : MarshalByRefObject
          {
                Assembly assembly = null;
                public void LoadAssembly()
                {
                      assembly = Assembly.LoadFile(@"TestDLL.dll");
                }
                public bool Invoke(string fullClassName, string methodName, params Object[] args)
                {
                      if(assembly == null)
                            return false;
                      Type tp = assembly.GetType(fullClassName);
                      if (tp == null)
                            return false;
                      MethodInfo method = tp.GetMethod(methodName);
                      if (method == null)
                            return false;
                      Object obj = Activator.CreateInstance(tp);
                      method.Invoke(obj, args);
                      return true;
                 }
          }
    }
    

    注意:

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

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

  • 相关阅读:
    宏任务与微任务
    reactnative 自定义项目的图标库
    react-native中textInput在androidTV上的焦点处理(坑篇)
    js中!!的运用
    ES6里class杂乱随笔
    浅析链式调用
    link和@import的区别
    ES2020链判断运算符?.和Null判断运算符??
    vue组件使用name属性来生成递归组件
    k8s学习——相关概念
  • 原文地址:https://www.cnblogs.com/gt1987/p/13638248.html
Copyright © 2011-2022 走看看