本节需要了解:
- 什么是System.AppDomain类,可以使用该类做什么
- .Net运行库是如何监控线程的
1. System.AppDomain 类
之前了解,进程是内存和资源的一个物理独立的部分,后来又提到一个进程至少有一个线程。
当Microsoft设计.Net Framewrok时,新增了一个隔离层,称为应用程序域或AppDomain。应用程序不是进程那样是一个物理独立的部分,而是进程内部的一个逻辑独立部分。在一个进程中可以存在多个应用程序域。通常,标准的进程不使用代理,就不可以访问其它进程的数据(之前说明了,进程都是独立的操作数据的).使用代理会耗费很多系统资源,编码也会变得很复杂。但是引入应用程序的概念,就可以在同一个进程中启动好几个应用程序域,进程所提供的同一个隔离区域可以和应用程序一起使用。线程可以跨多个应用程序域来执行,且不需要耗费因线程内部通信而产生的系统开销。
Microsoft把这些应用程序域的所有功能都封装在一个名为System.AppDomain的类中。为了了解一下System.AppDomain
1.1.设置AppDomain数据
创建一个控制台MyAppDomain:
namespace MyAppDomain { public class Program { public AppDomain Domain; public int ThreadId; public void SetDomainDate(string vName, string vValue) { Domain.SetData(vName, (object)vValue); ThreadId = AppDomain.GetCurrentThreadId(); } public T GetDomainData<T>(string vName) { return (T)Domain.GetData(vName); } static void Main(string[] args) { var DataName = "MyData"; var DataValue = "Some Data"; Console.WriteLine("开始"); Program obj = new Program(); obj.Domain = AppDomain.CurrentDomain; obj.SetDomainDate(DataName, DataValue); Console.WriteLine("输出System.AppDomain数据"); Console.WriteLine(string.Format("数据存储的key:{0},取出的值为:{1},获取当前线程id:{2}",DataName,obj.GetDomainData<string>(DataName),obj.ThreadId)); Console.Read(); } } }
执行之后会输出:
开始
输出System.AppDomain数据
数据存储的key:MyData,取出的值为:Some Data,获取当前线程id:22388(ThreadId是根据当前系统环境自动分配而获取)
1.2.在指定的应用程序域中执行代码
创建新的应用程序域,着重介绍在新建的AppDomain中创建新线程时的有关行为,源代码:
namespace CreateAppDomians { class Program { static void Main(string[] args) { AppDomain DomainA; DomainA = AppDomain.CreateDomain("MyDomainA");//创建一个应用程序域,域名为:MyDomainKeyA string stringA = "值1"; DomainA.SetData("DomainKey", stringA); CommonCallBack();//直接调用这方法会创建一个新应用程序域,这时候就有两个应用程序了 CrossAppDomainDelegate delegateA = new CrossAppDomainDelegate(CommonCallBack);//这里使用跨应用程序域的调用的 DomainA.DoCallBack(delegateA);//委托把另一程序域委托给由指定的应用程序执行 Console.Read(); } public static void CommonCallBack() { AppDomain Domain; Domain = AppDomain.CurrentDomain; Console.WriteLine("获取当前线程保存的值value:'{0}',获取可执行程序的名称:{1},ThreadId:{2}", Domain.GetData("DomainKey"), Domain.FriendlyName.ToString(), AppDomain.GetCurrentThreadId()); } } }
输出的结果为:
获取当前线程保存的值value:'',获取可执行程序的名称:CreateAppDomians.exe,ThreadId:13007
获取当前线程保存的值value:'值1',获取可执行程序的名称:MyDomainA,ThreadId:13007
从再次调用GetCurrentThreadId()方法。在程序输出结果中,无论当中的应用程序执行,得到的线程的ID都是一样的。这一点很重要,因为这不仅意味着AppDomain可以有零个或多个线程,而且能够跨不同的域来执行线程。
2.线程管理与.net运行库
.Net Framework不仅提供了对自由线程的进程和逻辑应用程序的支持,还提供了处理线程的对象,这些对象表示都是System.Threading.Thread类的实例。而在了解System.Threading.Thread之前先要了解托管线程和非托管线程的关系。 说得笼统点,非托管线程(在.net外部创建)是如何与托管线程类的实例(表示运行在.net CRL内部的线程)发生关联。
当非托管线程在托管线程中执行时,运行库会检查TLS(线程本地存储器),确定是否存在托管的对象。如果找到了一个托管线程对象,运行库就使用那个线程。如果没有的就创建一个并使用。这个过程很简单,但是必要的。因为运行库不为这类内部调用和创建线程,就不能在托管的环境中标识线程,更不能控制它了。