同一进程间的线程虽然共享资源和内存块,但仍然拥有自己的上下文。在.NET中,线程的上下文有流动的特性。为了回答此类面试题,应聘者需要对线程的上下文以及其流动机制有基本的认识。
所涉及的知识点
线程执行上下文的内容
上下文的流动
如何阻止上下文的传递
分析问题
1.线程的执行上下文
在.NET中,每一个线程都会包含一个执行上下文,执行上下文是指线程运行中某时刻的上下文概念,可以说它是一个动态过程的快照。定义在System.Threading中的ExecutionContext类型代表了一个执行上下文。执行上下文会包含下列内容:
安全上下文
调用上下文
同步上下文
本地化上下文
事务上下文
CLR宿主上下文
正如读者所看到的,线程的执行上下文相当于一个所有上下文的打包类型,在通常情况下,我们可以把所有这些综合称为线程的上下文。
2.上下文的流动
当程序中新建一个线程时,执行上下文会自动地从当前线程流入到新建的线程之中,这样做可以保证新建的线程天生具有和主线程相同的安全设置和文化设置。代码7-10通过修改安全上下文,来展示了线程上下文的流动性。代码中会使用到定义在ExecutionContext类型中的Capture方法,该方法被用来捕获当前线程的执行上下文。
在main方法中,建立了一个子线程,并且通过改变主线程的访问文件权限来查看权限上下文流动到子线程中的状况,如代码7-10所示。
代码7-10 线程执行上下文流动:ExecutionContextFlow.cs
partial class ExecutionContextFlow { const String _testFile = "C:\\TestContext.txt"; static void Main(string[] args) { try { //建立测试文件 CreateTestFile(); //测试当前线程安全上下文 Console.WriteLine("主线程权限测试:"); TestPermission(null); //建立一个子线程 Console.WriteLine("子线程权限测试:"); Thread son1 = new Thread(TestPermission); son1.Start(); son1.Join(); //现在修改安全上下文 //阻止文件访问 FileIOPermission fip = new FileIOPermission (FileIOPermissionAccess.AllAccess, _testFile); fip.Deny(); Console.WriteLine("已阻止文件访问"); //测试当前线程安全上下文 Console.WriteLine("主线程权限测试:"); TestPermission(null); //建立一个子线程 Console.WriteLine("子线程权限测试:"); Thread son2 = new Thread(TestPermission); son2.Start(); son2.Join(); //现在修改安全上下文 //恢复文件访问 SecurityPermission.RevertDeny(); Console.WriteLine("已恢复文件访问"); //测试当前线程安全上下文 Console.WriteLine("主线程权限测试:"); TestPermission(null); //建立一个子线程 Console.WriteLine("子线程权限测试:"); Thread son3 = new Thread(TestPermission); son3.Start(); son3.Join(); Console.Read(); } finally { //删除测试文件 DeleteTestFile(); } } }
|
在main方法中用到了一些辅助方法,包括文件的建立、删除和文件访问权限的检查,代码7-11定义了这些辅助方法。
代码7-11 线程执行上下文流动:ExecutionContextFlow.cs
/// <summary> /// 辅助方法 /// </summary> partial class ExecutionContextFlow { /// <summary> /// 建立测试文件 /// </summary> static void CreateTestFile() { if (!File.Exists(_testFile)) { using (FileStream fs = File.Create(_testFile)) { } } } /// <summary> /// 清除测试文件 /// </summary> static void DeleteTestFile() { try { if (File.Exists(_testFile)) File.Delete(_testFile); } finally { } } /// <summary> /// 尝试访问测试文件来测试安全上下文 /// </summary> /// <param name="state">状态变量</param> static void TestPermission(object state) { try { //尝试访问文件 File.GetCreationTime(_testFile); //如果没有异常抛出,则测试通过 Console.WriteLine("权限测试通过"); } catch (SecurityException) { //表明没有权限访问 Console.WriteLine("权限测试没有通过"); } } }
|
回顾上述代码,程序通过FileIOPermission对象来控制主线程对文件的访问权限,并且新建子线程来查看主线程的安全上下文的改变是否影响到了子线程。正如笔者介绍的,主线程的安全上下文作为执行上下文的一部分将传递给子线程,下面是程序的执行结果:
主线程权限测试:
权限测试通过
子线程权限测试:
权限测试通过
已阻止文件访问
主线程权限测试:
权限测试没有通过
子线程权限测试:
权限测试没有通过
已恢复文件访问
主线程权限测试:
权限测试通过
子线程权限测试:
权限测试通过
3.阻止执行上下文的流动
执行上下文的流动确保了所有线程和主线程保持一致的上下文,但有的时候,系统也会需要子线程完全拥有新的上下文。撇去功能上的需求,执行上下文的流动确实使得程序的执行效率下降很多,线程上下文的包装是一个成本较高的工作,而有的时候这样的包装并不是必需的。
在这种情况下,程序员就需要手动地防止线程上下文的流动,常用的有下列两种方法:
使用定义在System.Threading.ThreadPool类型中的UnsafeQueueUserWorkItem方法
使用定义在ExecutionContext类型中的SuppressFlow方法
代码7-12展示了如何阻止执行上下文的流动,以及这样做的效果,它使用了和代码7-11相同的辅助方法,这里不再列出。
代码7-12 阻止线程执行上下文流动:SuppressContextFlow.cs
partial class SuppressContextFlow { const String _testFile = "C:\\TestContext.txt"; static void Main(string[] args) { try { //建立测试文件 CreateTestFile(); //现在修改安全上下文 //阻止文件访问 FileIOPermission fip = new FileIOPermission (FileIOPermissionAccess.AllAccess, _testFile); fip.Deny(); Console.WriteLine("已阻止文件访问"); //使用UnsafeQueueUserWorkItem方法 Console.WriteLine("主线程权限测试:"); TestPermission(null); //建立一个子线程 Console.WriteLine("子线程权限测试:"); ThreadPool.UnsafeQueueUserWorkItem(TestPermission, null); Thread.Sleep(1000); //使用SuppressFlow方法 using (AsyncFlowControl afc = ExecutionContext.SuppressFlow()) { //测试当前线程安全上下文 Console.WriteLine("主线程权限测试:"); TestPermission(null); //建立一个子线程 Console.WriteLine("子线程权限测试:"); Thread son1 = new Thread(TestPermission); son1.Start(); son1.Join(); } //现在修改安全上下文 //恢复文件访问 SecurityPermission.RevertDeny(); Console.WriteLine("已恢复文件访问"); //测试当前线程安全上下文 Console.WriteLine("主线程权限测试:"); TestPermission(null); //建立一个子线程 Console.WriteLine("子线程权限测试:"); Thread son2 = new Thread(TestPermission); son2.Start(); son2.Join(); Console.Read(); } finally { //删除测试文件 DeleteTestFile(); } } }
|
下面是这段代码的执行结果:
已阻止文件访问
主线程权限测试:
权限测试没有通过
子线程权限测试:
权限测试通过
主线程权限测试:
权限测试没有通过
子线程权限测试:
权限测试通过
已恢复文件访问
主线程权限测试:
权限测试通过
子线程权限测试:
权限测试通过
正如读者看到的,通过前面介绍的两种方法,有效地阻止了主线程的执行上下文流动到新建的线程之中,这样的机制对性能的提高是显著的。
阻止线程执行上下文的流动会潜在地提高子线程的安全权限,这是因为主线程中的安全限制没有被附加到子线程之上。所以读者在设计的时候应该充分考虑到这个问题,在保证不会影响到系统安全性的前提下再着手提高其效率。
答案
线程的执行上下文是所有线程上下文的一个包装,在通常情况下,当前线程的执行上下文会自动地流入到新建的线程之中。程序员可以使用定义在System.Threading.ThreadPool类型中的UnsafeQueueUserWorkItem方法和定义在ExecutionContext类型中的SuppressFlow方法来阻止这样的流动。
……
转载自:http://book.51cto.com/art/200812/99977.htm