[草稿] Managed COM Apartment - ExecutionContext [1]
Posted on 2005-12-30 01:36 Flier Lu 阅读(4572) 评论(5) 编辑 收藏 举报.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)上下文、最后执行线程等信息。
其包含主要信息的伪代码如下:

2

3

4

5

6

7

8

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 运行代码:

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

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() 方法来屏蔽,示例如下:

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

test 2 因为执行上下文缺省自动传播性显式失败;而调用 SuppressFlow 方法后 test 3 将成功;最终 Undo 恢复到原本的传播设置,导致 test 4 显式失败。值得注意的是调用 Undo 的线程必须跟调用 SuppressFlow 方法的线程相同,并且具有相同的执行上下文。
除了 Capture 之外,还可以直接通过 Thread.ExecutionContext 属性获取线程的执行上下文。但这种方式获取的执行上下文被关联到特定线程,不能直接在其它线程中使用,示例如下:

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

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...
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix