C# 应用程序域和程序集

一、进程Process:

    进程(Process)是Windows系统中的一个基本概念,它包含着一个运行程序所需要的资源。进程之间是相对独立的,一个进程无法直接访问另一个进程的数据(除非利用分布式计算方式),一个进程运行的失败也不会影响其他进程的运行,Windows系统就是利用进程把工作划分为多个独立的区域的。进程可以理解为一个程序的基本边界。

1.进程的建立与销毁:

static void Main(string[] args)
         {
             Process process = Process.Start("notepad.exe","File.txt");
             Thread.Sleep(2000);
             process.Kill();
        }
2.例出计算机运行中的进程:

 static void Main(string[] args)
          {
             var processList = Process.GetProcesses()
                  .OrderBy(x=>x.Id)
                 .Take(10);
              foreach (var process in processList)
                 Console.WriteLine(string.Format("ProcessId is:{0} \t ProcessName is:{1}",
                      process.Id, process.ProcessName));
              Console.ReadKey();
         }
可以通过 GetProcessById 方法获取对应的进程,也可能通过GetProcessByName方法获取多个对应名称的进程
3.获取进程中的所有模块module:这些模块可以是以 *.dll 结尾的程序集,也可以是 *.exe 结尾的可执行程序

 static void Main(string[] args)
         {
            var moduleList = Process.GetCurrentProcess().Modules;
             foreach (System.Diagnostics.ProcessModule module in moduleList)
                 Console.WriteLine(string.Format("{0}\n  URL:{1}\n  Version:{2}",
                     module.ModuleName,module.FileName,module.FileVersionInfo.FileVersion));
            Console.ReadKey();
         }

二、程序集:

1.定义:程序集是包含一个或多个类型定义文件和资源文件的集合。它允许我们分离可重用类型的逻辑表示和物理表示。

2.功能:

2.1.包含公共语言运行库执行的代码,如果可移植可执行 (PE) 文件没有相关联的程序集清单,则将不执行该文件中的 Microsoft 中间语言 (MSIL) 代码。请注意,每个程序集只能有一个入口点(即 DllMain、WinMain 或 Main)。

2.2.程序集形成安全边界。程序集就是在其中请求和授予权限的单元

2.3.程序集形成类型边界。每一类型的标识均包括该类型所驻留的程序集的名称。在一个程序集范围内加载的 MyType 类型不同于在其他程序集范围内加载的 MyType 类型。

2.4.程序集形成引用范围边界。程序集的清单包含用于解析类型和满足资源请求的程序集元数据。它指定在该程序集之外公开的类型和资源。该清单还枚举它所依赖的其他程序集。

2.5.程序集形成版本边界。程序集是公共语言运行库中最小的可版本化单元,同一程序集中的所有类型和资源均会被版本化为一个单元。程序集的清单描述您为任何依赖项程序集所指定的版本依赖性。

2.6.程序集形成部署单元。当一个应用程序启动时,只有该应用程序最初调用的程序集必须存在。其他程序集(例如本地化资源和包含实用工具类的程序集)可以按需检索。这就使应用程序在第一次下载时保持精简。

2.7.程序集是支持并行执行的单元.

    程序集可以是静态的或动态的。静态程序集可以包括 .NET Framework 类型(接口和类),以及该程序集的资源(位图、JPEG 文件、资源文件等)。静态程序集存储在磁盘上的可移植可执行 (PE) 文件中。您还可以使用 .NET Framework 来创建动态程序集,动态程序集直接从内存运行并且在执行前不存储到磁盘上。您可以在执行动态程序集后将它们保存在磁盘上。

    有几种创建程序集的方法。您可以使用过去用来创建 .dll 或 .exe 文件的开发工具,例如 Visual Studio .NET。您可以使用在 .NET Framework SDK 中提供的工具来创建带有在其他开发环境中创建的模块的程序集。您还可以使用公共语言运行库 API(例如 Reflection.Emit)来创建动态程序集。


3.全局程序集缓存:

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

    应当仅在需要时才将程序集安装到全局程序集缓存中以进行共享。 一般原则是:程序集依赖项保持专用,并在应用程序目录中定位程序集,除非明确要求共享程序集。 另外,不必为了使 COM 互操作或非托管代码可以访问程序集而将程序集安装到全局程序集缓存

   有两种方法可以将程序集部署到全局程序集缓存中:

    a. 使用专用于全局程序集缓存的安装程序。 该方法是将程序集安装到全局程序集缓存的首选方法。
     b.使用 Windows 软件开发包 (SDK) 提供的名为全局程序集缓存工具 (Gacutil.exe) 的开发工具。

三、应用程序域:

    使用.NET建立的可执行程序 *.exe,并没有直接承载到进程当中,而是承载到应用程序域(AppDomain)当中.应用程序域是.NET引入的一个新概念,它比进程所占用的资源要少,可以被看作是一个轻量级的进程。

    在一个进程中可以包含多个应用程序域一个应用程序域可以装载一个可执行程序(*.exe)或者多个程序集(*.dll),这样可以使应用程序域之间实现深度隔离,即使进程中的某个应用程序域出现错误,也不会影响其他应用程序域的正常运作。

    当一个程序集同时被多个应用程序域调用时,会出现两种情况:
    第一种情况:CLR分别为不同的应用程序域加载此程序集。
    第二种情况:CLR把此程序集加载到所有的应用程序域之外,并实现程序集共享,此情况比较特殊,被称作为Domain Neutral.


1.创建自定义的应用程序域:

    当需要应用程序域时,公共语言运行时宿主会自动创建它们。 不过,您可以创建自己的应用程序域并将它们加载到需要亲自管理的程序集中。 您也可以创建从中http://write.blog.csdn.net/postedit?type=edit执行代码的应用程序域。

namespace AttributeTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Create new domain");
            AppDomain domain = AppDomain.CreateDomain("MyDomain");
            Console.WriteLine("Host domain: "+AppDomain.CurrentDomain.FriendlyName);//Host domain: AttributeTest.vshost.exe
            Console.WriteLine("Child domain:"+domain.FriendlyName);// Child domain: MyDomain
            Console.ReadKey();
        }

    }
}

2.卸载应用程序域:

    当您完成使用应用程序域时,可使用 System.AppDomain.Unload 方法将其卸载。 Unload 方法会正常关闭指定的应用程序域。 卸载过程中,没有新线程可以访问该应用程序域,并且会释放该应用程序域特定的所有数据结构。加载到应用程序域中的所有程序集都会被移除,无法再使用,如果应用程序域中的程序集不是特定于域的,则程序集的数据会保留在内存中,直到整个进程关闭。除了关闭整个进程,没有机制可以卸载非特定于域的程序集。 在某些情况下,卸载应用程序域的请求不起作用,并导致 CannotUnloadAppDomainException。

         Console.WriteLine("Creating new AppDomain.");
            AppDomain domain = AppDomain.CreateDomain("MyDomain", null);

            Console.WriteLine("Host domain: " + AppDomain.CurrentDomain.FriendlyName);
            Console.WriteLine("child domain: " + domain.FriendlyName);
            AppDomain.Unload(domain);
            try
            {
                Console.WriteLine();
                Console.WriteLine("Host domain: " + AppDomain.CurrentDomain.FriendlyName);
                // The following statement creates an exception because the domain no longer exists.
                Console.WriteLine("child domain: " + domain.FriendlyName);
            }
            catch (AppDomainUnloadedException e)
            {
                Console.WriteLine(e.GetType().FullName);
                Console.WriteLine("The appdomain MyDomain does not exist.");
            }
            Console.ReadKey();

3.配置应用程序域:

    可以使用 AppDomainSetup 类为新的应用程序域提供带有配置信息的公共语言运行时,AppDomainSetup的ApplicationBase属性定义应用程序的根目录,当CLR需要满足类型请求时,

它在ApplicationBase属性指定的目录中探测包含该类型的程序集。

            AppDomainSetup domainInfo = new AppDomainSetup();
            domainInfo.ApplicationBase = "C:\Users\v-lozhu\Documents\Visual Studio 2015\Projects\MyTestProjects\AttributeTest";
            AppDomain domain = AppDomain.CreateDomain("MyDomain",null,domainInfo);

            Console.WriteLine("Host domain: " + AppDomain.CurrentDomain.FriendlyName);
            Console.WriteLine("child domain: " + domain.FriendlyName);
            Console.WriteLine("Application base is: " + domain.SetupInformation.ApplicationBase);
            // Unload the application domain.
            Console.ReadKey();
4.将程序集加载到应用程序域中:

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

a.Assembly.Load(assemblyName);使用程序集名来加载到应用程序域中,为常用方法。

b.Assembly.LoadFrom():加载给定其文件位置的程序集,通过此方法加载程序集将使用不同的加载上下文。

c.Assembly.ReflectionOnlyLoad和 Assembly.ReflectionOnlyLoadFrom:将程序集加载到仅反射上下文中,可检查(但不能执行)加载到该上下文中的程序集,同时允许检查以其他平台为目标的程序集。

d.AppDomain的CreateInstance和CreateInstanceAndUnwrap方法。

e.Type.GetType()加载程序集

f.AppDomain的Load方法加载程序集。主要用于实现COM互操作性,不应该使用该方法将程序集加载到除从其调用该方法的应用程序域以外的其他应用程序域。

namespace AttributeTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Assembly a = <strong>Assembly.Load</strong>("ExampleTest.exe");
            Type myType = a.GetType("ExampleTest.Example");//Name = "Example" FullName = "ExampleTest.Example"
            MethodInfo myMethod = myType.GetMethod("MethodA");
            object obj = Activator.CreateInstance(myType);
            myMethod.Invoke(obj, null);
            Console.ReadKey();
        }

    }
}

 static void Main(string[] args)
         {
            var appDomain = AppDomain.CreateDomain("NewAppDomain");
             <strong>appDomain.Load</strong>("Model");
             foreach (var assembly in appDomain.GetAssemblies())
                 Console.WriteLine(string.Format("{0}\n----------------------------",
                    assembly.FullName));
             Console.ReadKey();
         }
//<p>//当需要在AppDomain加载可执行程序时,可以使用ExecuteAssembly方法。</p>AppDomain.ExecuteAssembly("Example.exe")

       static void Main(string[] args)
        {
          var person = (Person<strong>)AppDomain.CurrentDomain.CreateInstance</strong>("Model", "Model.Person").Unwrap();
            person.ID = 1;
            person.Name = "ddd";
            person.Age = 29;
            Console.WriteLine(person.Name);
            Console.ReadKey();
        }


5.从程序集中获得类型和成员信息:

            Type MyType = Type.GetType("System.IO.BinaryReader");
            MemberInfo[] infos = MyType.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly);
            Console.WriteLine("members count "+infos.Length);
            Console.WriteLine("full name "+MyType.FullName);
            foreach (MemberInfo item in infos)
            {
                Console.WriteLine("\n"+item.Name);
            }
            Console.ReadKey();

6.影像复制程序集:

    公共语言运行时会在加载程序集时锁定程序集文件,因此只有卸载此应用程序域才能更新此程序集,借助卷影复制,无需卸载应用程序域就可更新用于此应用程序域的程序集。

    应用程序域被配置为卷影复制时,应用程序路径中的程序集复制到了另一个位置,并从此位置加载,此副本处于锁定状态,但原始程序集文件已解锁并可进行更新。

    只有存储在应用程序目录或其子目录中的程序集才可进行卷影复制,这些程序集在配置应用程序域时由 ApplicationBase 和 PrivateBinPath 指定。 存储在全局程序集缓存中的程序集不可进行卷影复制。

6.1.启用卷影复制:

  AppDomainSetup domainInfo = new AppDomainSetup();
  domainInfo.ShadowCopyFiles = "true";


四、.NET 上下文Context:

    1.应用程序域是进程中承载程序集的逻辑分区,在应用程序域中存在更细粒度的用于承载.NET对象的实体,即.NET上下文Context.所有的.NET对象都存在于.NET上下文当中,每个AppDomain当中至少存在一个默认上下文(context0),:一般类所建立的对象为上下文灵活对象(context-agile),它们都由CLR自动管理,可存在于任意的上下文当中。

                    

2.透明代理:

    在上下文的接口当中存在着一个消息接收器负责检测拦截和处理信息,当对象是MarshalByRefObject的子类的时候,CLR将会建立透明代理,实现对象与消息之间的转换。
    应用程序域是CLR中资源的边界,一般情况下,应用程序域中的对象不能被外界的对象所访问。而MarshalByRefObject 的功能就是允许在支持远程处理的应用程序中跨应用程序域边界访问对象,在使用.NET Remoting远程对象开发时经常使用到的一个父类。

3.上下文绑定:

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

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

 class Program
    {
        static void Main(string[] args)
        {
            Example example = new Example();
            example.Test();
            MycontextBound bouind = new MycontextBound();
            bouind.Test("ContextBound Test");
            example.Sync(bouind);
            Console.ReadKey();
        }

        public class Example
        {
            public void Test()
            {
                ContextMessage("Example test\n");
            }
            public void Sync(<strong>MycontextBound contextBound</strong>)//传入MyContextBound的代理
            {
                contextBound.Test("Example call on ContextBound\n");
            }
        }

        [Synchronization]
        public class MycontextBound : ContextBoundObject
        {
            public void Test(string message)
            {
                ContextMessage(message);
            }
        }

        //显示上下文信息
        public static void ContextMessage(string data)
        {
            Context context = Thread.CurrentContext;
            Console.WriteLine(string.Format("{0}ContextId is {1}", data, context.ContextID));
            foreach (var prop in context.ContextProperties)
                Console.WriteLine(prop.Name);
            Console.WriteLine();
        }

    }

如果在Example类中直接用MycontextBound的示例调用其test方法, 则无法调用,只能通过代理来访问MycontextBound对象。

五、进程、线程、AppDomain、.NET上下文的关系:

1.跨AppDomain运行代码:

    在应用程序域之间的数据是相对独立的,当需要在其他AppDomain当中执行当前AppDomain中的程序集代码时,可以使用CrossAppDomainDelegate委托。把CrossAppDomainDelegate委托绑定方法以后,通过AppDomain的DoCallBack方法即可执行委托.

 class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("CurrentAppDomain start!");
            //建立新的应用程序域对象
            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);
            Console.ReadKey();

        }
        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));
        }
    }
2.跨AppDomain的线程:

    线程存在于进程当中,它在不同的时刻可以运行于多个不同的AppDomain当中。它是进程中的基本执行单元,在进程入口执行的第一个线程被视为这个进程的主线程。在.NET应用程序中,都是以Main()方法作为入口的,当调用此方法时 系统就会自动创建一个主线程。线程主要是由CPU寄存器、调用栈和线程本地存储器(Thread Local Storage,TLS)组成的。CPU寄存器主要记录当前所执行线程的状态,调用栈主要用于维护线程所调用到的内存与数据,TLS主要用于存放线程的状态信息。

    首先建立一个ConsoleApplication项目,在执行时输入当前线程及应用程序域的信息,最后生成Example.exe的可执行程序。

class Program
    {
        static void Main(string[] args)
        {
            var message = string.Format("  CurrentThreadID is:{0}\tAppDomainID is:{1}",
                Thread.CurrentThread.ManagedThreadId, AppDomain.CurrentDomain.Id);
            Console.WriteLine(message);
            Console.Read();
        }
    }
 然后再新建一个ConsoleApplication项目,在此项目中新一个AppDomain对象,在新的AppDomain中通过ExecuteAssembly方法执行Example.exe程序。(将Example生成的bin目录下的文件放到当前项目bin目录下):

 class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Current Appdomain start!");
            ShowMessage();
            //建立新的应用程序域对象
            AppDomain newAppDomain = AppDomain.CreateDomain("newAppDomain");
            newAppDomain.ExecuteAssembly("Example.exe");
            AppDomain.Unload(newAppDomain);
            Console.ReadKey();
        }

        public static void ShowMessage()
        {
            var message = string.Format("  CurrentThreadID is:{0}\tAppDomainID is:{1}",
                Thread.CurrentThread.ManagedThreadId, AppDomain.CurrentDomain.Id);
            Console.WriteLine(message);
        }
    }
输出:

 Current Appdomain start!
  CurrentThreadID is:8  AppDomainID is:1
  CurrentThreadID is:8  AppDomainID is:2

ID等于8的线程在不同时间内分别运行于AppDomain 1与AppDomain 2当中。

4.3.跨上下文的线程:

线程既然能够跨越AppDomain的边界,当然也能跨越不同的上下文。下例,线程将同时运行在默认上下文与提供安全线程的上下文中。

class Program
    {
        static void Main(string[] args)
        {
            //当前应用程序域信息
            Console.WriteLine("CurrentAppDomain start!");
            ShowMessage();

            //在上下文绑定对象中运行线程
            ContextBound contextBound = new ContextBound();
            contextBound.Test();
            Console.ReadKey();
        }

        public static void ShowMessage()
        {
            var message = string.Format("  CurrentThreadID is:{0}\tAppDomainID is:{1}",
                Thread.CurrentThread.ManagedThreadId, Thread.CurrentContext.ContextID);
            Console.WriteLine(message);
        }

        [Synchronization]
        public class ContextBound : ContextBoundObject
        {
            public void Test()
            {
                ShowMessage();
            }
        }
    }
输出:

  CurrentAppDomain start!
  CurrentThreadID is:10 AppDomainID is:0
  CurrentThreadID is:10 AppDomainID is:1

     进程(Process)、线程(Thread)、应用程序域(AppDomain)、上下文(Context)的关系如图5.0,一个进程内可以包括多个应用程序域,也有包括多个线程,线程也可以穿梭于多个应用程序域当中。但在同一个时刻,线程只会处于一个应用程序域内。线程也能穿梭于多个上下文当中,进行对象的调用











  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值