Flier's Sky

天空,蓝色的天空,眼睛看不到的东西,眼睛看得到的东西

导航

[草稿] Managed COM Apartment - ExecutionContext [1]

Posted on 2005-12-30 01:36  Flier Lu  阅读(4569)  评论(5编辑  收藏  举报
原文:http://www.blogcn.com/User8/flier_lu/blog/27498923.html

      .NET 2.0 在底层实现上的一个非常重要的改进,是在引入了 ExecutionContext 的概念。从而引入了一个类似 COM 中套间 (Apartment) 概念的一个逻辑上的执行环境。这在很大程度上改变了 .NET 1.x 中对线程以及其执行环境的管理机制,并进而可以实现非常灵活的控制机制。因为此概念涉及到的方面非常庞杂,且现阶段可参考的资料非常少,笔者希望能通过一个系列文章,对其进行一个较为全面的分析。    
    
      首先,ExecutionContext 是一个容器,MSDN 如是说:

  The ExecutionContext class provides a single container for all information relevant to a logical thread of execution. This includes security context, call context, synchronization context, localization context, and transaction context.
    
      也就是说在 ExecutionContext 中,包括了一个逻辑线程执行代码所需要的各种上下文信息。除了文档中明确指出的安全、调用、同步、本地化和事务上下文外,实现上还可提供 CLR 宿主(Host)上下文、最后执行线程等信息。
      其包含主要信息的伪代码如下:

1public sealed class ExecutionContext 
2{
3  internal HostExecutionContext HostExecutionContext getset; }
4  internal IllogicalCallContext IllogicalCallContext getset; }
5  internal LogicalCallContext LogicalCallContext getset; }
6  internal SecurityContext SecurityContext set; }
7  internal SynchronizationContext SynchronizationContext getset; }
8  internal Thread Thread set; }
9}


      其次,ExecutionContext 是一个快照,MSDN 如是说:

     The ExecutionContext class provides the functionality for user code to capture and transfer this context across user-defined asynchronous points. The common language runtime ensures that the ExecutionContext is consistently transferred across runtime-defined asynchronous points within the managed process. 

      ExecutionContext 的一个典型应用场景,是捕获 (capture) 执行环境,并将其传递给一个用户自定义的异步执行点。CLR 确保在使用此 ExecutionContext 的异步执行点,能获得与捕获快照相一致的行为。
例如以下代码演示了如何使用 ExecutionContext.Capture 捕获执行环境快照,并在其环境中通过 ExecutionContext.Run 运行代码:

 1namespace nsfocus
 2{
 3  class ExecutionContextSample
 4  {
 5    static void DemandPermission()
 6    {
 7      try
 8      {
 9        Console.WriteLine("In the thread executing a Demand for FileDialogPermission.");
10        new FileDialogPermission(FileDialogPermissionAccess.OpenSave).Demand();
11        Console.WriteLine("Successfully demanded FileDialogPermission.");
12      }

13      catch (Exception e)
14      {
15        Console.WriteLine(e.Message);
16      }

17    }

18
19    private static void testPermission(object state)
20    {
21      Thread t1 = new Thread(new ThreadStart(DemandPermission));
22      t1.Start();
23      t1.Join();
24    }

25
26    static void Main()
27    {
28      try
29      {
30        FileDialogPermission fdp = new FileDialogPermission(FileDialogPermissionAccess.OpenSave);
31
32        testPermission(null); // test 1
33
34        ExecutionContext ctxt = ExecutionContext.Capture();
35
36        fdp.Deny();
37
38        testPermission(null); // test 2
39
40        ExecutionContext.Run(ctxt, new ContextCallback(testPermission), null); // test 3
41
42        testPermission(null); // test 4
43      }

44      catch (Exception e)
45      {
46        Console.WriteLine(e.Message);
47      }

48    }
  
49  }

50}


      缺省情况下本机代码具有打开文件对话框的权限,因此 test 1 将显式成功信息;而一旦我们显式调用了 CodeAccessPermission.Deny 方法,CLR 的安全管理器会在调用堆栈的最后一个安全帧中,设置相应标志禁用此权限。(详细的分析可以参考我 blog 上另外一篇文章《CLR 中代码访问安全检测实现原理》)
因此 test 2 和 4 的执行将显式异常信息,类似如下:

In the thread executing a Demand for FileDialogPermission.
Request for the permission of type 'System.Security.Permissions.FileDialogPermis
sion, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e08
9' failed.


         test 3 虽然是在 2 和 4 之间调用,但其通过 ExecutionContext.Run 方法,以 Deny 权限之前的执行上下文执行,因此会显式成功信息。与安全权限类似,执行上下文还可以提供对同步、事务等等更多类型上下文的支持。

      而通过上述例子我们也可以发现,即使是切换线程的调用,当前执行环境本身也会在 Thread.Start 方法被调用时,传播到新建的线程中。唯一的特例是调用 ThreadPool.UnsafeQueueUserWorkItem 方法新增线程池任务,因为对这种情况下无法将 CLR 的调用堆栈保留,任务将直接在完成端口 (IOCP) 的新建 CLR 上下文中执行。当然也可以调用 Capture 和 CreateCopy 方法获取目标执行上下文,并在线程池工作方法中通过 Run 来执行。

      如果要禁用这一执行上下文自动传播的语义,可以通过 ExecutionContext.SuppressFlow() 方法来屏蔽,示例如下:

 1static void Main()
 2{
 3  try
 4  {
 5    FileDialogPermission fdp = new FileDialogPermission(
 6        FileDialogPermissionAccess.OpenSave);
 7
 8    testPermission(null); // test 1
 9
10    ExecutionContext ctxt = ExecutionContext.Capture();
11
12    fdp.Deny();
13
14    testPermission(null); // test 2
15
16    AsyncFlowControl aFC = ExecutionContext.SuppressFlow();
17
18    testPermission(null); // test 3
19
20    aFC.Undo();
21
22    testPermission(null); // test 4
23  }

24}


      test 2 因为执行上下文缺省自动传播性显式失败;而调用 SuppressFlow 方法后 test 3 将成功;最终 Undo 恢复到原本的传播设置,导致 test 4 显式失败。值得注意的是调用 Undo 的线程必须跟调用 SuppressFlow 方法的线程相同,并且具有相同的执行上下文。

      除了 Capture 之外,还可以直接通过 Thread.ExecutionContext 属性获取线程的执行上下文。但这种方式获取的执行上下文被关联到特定线程,不能直接在其它线程中使用,示例如下:

 1class ExecutionContextSample
 2{
 3  private static ExecutionContext _ctxt;
 4
 5  private static void testContext()
 6  {
 7    try
 8    {
 9      ExecutionContext.Run(_ctxt, new ContextCallback(doDemandPermission), null);
10    }

11    catch (Exception e)
12    {
13      Console.WriteLine(e.Message);
14    }

15  }

16  
17    static void Main()
18    {
19      try
20      {
21        FileDialogPermission fdp = new FileDialogPermission(
22            FileDialogPermissionAccess.OpenSave);
23    
24        fdp.Deny();
25    
26        _ctxt = Thread.CurrentThread.ExecutionContext;
27    
28        Thread t1 = new Thread(new ThreadStart(testContext));
29    
30        t1.Start();
31        t1.Join();
32      }

33      catch (Exception e)
34      {
35        Console.WriteLine(e.Message);
36      }

37    }
        
38}


     这里因为直接在新线程里面,使用从主线程中通过 Thread.CurrentThread.ExecutionContext 属性获取的执行上下文,会导致一个 System.InvalidOperationException 的异常,错误信息如下

 Cannot apply a context that has been marshaled across AppDomains, that was not acquired through a Capture operation or that has already been the argument to a Set call.

  正确的方式是直接使用 ExecutionContext.Capture() 捕获执行上下文,或者调用 ExecutionContext.CreateCopy 方法复制线程的执行上下文。这两种方式获取的执行上下文都是与具体线程无关的。
  
  执行上下文的另一个重要作用,是用来存储同属一个逻辑线程的调用上下文中的变量。例如我们调用一个 COM+ 组件,并在组件中向调用上下文 (CallContext) 中存储数据。如果存储的数据实现了 ILogicalThreadAffinative 接口,则 CLR 会在调用者的执行上下文中提供此数据,并可通过 CreateCopy 方式传播到其它地方。
  
  以上我们大概从使用层面简单的介绍了执行上下文 (ExecutionContext),后续章节我们将从实现到应用进一步深入了解此概念的重要意义,尤其是在 .NET 2.0 环境下进行 COM+/Indigo 开发时,对此概念的理解能大大加深我们对技术的认识。
  
to be continue...