执行上下文

作者:追风剑情 发布于:2017-7-11 20:13 分类:C#

      每个线程都关联了一个执行上下文数据结构。执行上下文(execution context)包括的东西有安全设置(压缩栈、Thread的Principal属性和Windows身份)、宿主设置(参见System.Threading.HostExecutionContextManager)以及逻辑调用上下文数据(参见System.Runtime.Remoting.Messaging.CallContext的LogicalSetData和LogicalGetData方法)。线程执行它的代码时,一些操作会受到线程执行上下文设置(尤其是安全设置)的影响。理想情况下,每当一个线程(初始线程)使用另一个线程(辅助线程)执行任务时,前者的执行上下文应该流向(复制到)辅助线程。这就确保了辅助线程执行的任何操作使用的是相同的安全设置和宿主设置。还确保了在初始线程的逻辑调用上下文中存储的任何数据都适用于辅助线程。

      默认情况下,CLR自动造成初始线程的执行上下文“流向”任何辅助线程。这造成将上下文信息传给辅助线程,但这会对性能造成一定影响。这是因为执行上下文中包含大量信息,而收集所有这些信息,再把它们复制到辅助线程,要耗费不少时间。如果辅助线程又采用了更多的辅助线程,还必须创建和初始化更多的执行上下文数据结构。

System.Threading命名空间有一个ExecutionContext类,它允许你控制线程的执行上下文如何从一个线程“流”向另一个。下面展示了这个类的样子:


public sealed class ExecutionContext : IDisposable, ISerializable {
   [SecurityCritical]
   public static AsyncFlowControl SuppressFlow();
   public static void RestoreFlow();
   public static Boolean IsFlowSuppressed();
   //未列出不常用的方法
}
可用这个类阻止执行上下文流动以提升应用程序的性能。对于服务器应用程序,性能的提升可能非常显著。但客户端应用程序的性能提升不了多少。另外,由于SuppressFlow方法用[SecurityCritical]特性进行了标识,所以在某些客户端应用程序(比如Silverlight)中是无法调用的。当然,只有在辅助线程不需要或者不访问上下文信息时,才应阻止执行上下文的流动。如果初始线程的执行上下文不流向辅助线程,辅助线程会使用上一次和它关联的任意执行上下文。在这种情况下,辅助线程不应执行任何要依赖于执行上下文状态(比如用户的Windows身份)的代码。


下例展示了向CLR的线程池队列添加一个工作项的时候,如何通过阻止执行上下文的流动来影响线程逻辑调用上下文中的数据:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.Remoting.Messaging;

namespace ThreadPoolTest
{
    class Program
    {
        static void Main(string[] args)
        {
            // 将一些数据放到Main线程的逻辑调用上下文中
            CallContext.LogicalSetData("Name", "Jeffrey");

            // 初始化要由一个线程池线程做的一些工作,
            // 线程池线程能访问逻辑调用上下文数据
            ThreadPool.QueueUserWorkItem(state=>Console.WriteLine("Name={0}", CallContext.LogicalGetData("Name")));

            // 现在,阻止Main线程的执行上下文的流动
            ExecutionContext.SuppressFlow();

            // 初始化要由线程池线程做的工作,
            // 线程池线程不能访问逻辑调用上下文数据
            ThreadPool.QueueUserWorkItem(state => Console.WriteLine("Name={0}", CallContext.LogicalGetData("Name")));

            // 恢复Main线程的执行上下文的流动,
            // 以免将来使用更多的线程池线程
            ExecutionContext.RestoreFlow();

            Console.ReadLine();
        }
    }
}


11111.png

虽然我们讨论的是在调用ThreadPool.QueueUserWorkItem时阻止执行上下文的流动,但在使用Task对象时,以及在发起异步I/O操作时,这个技术同样有用。

注意 添加到逻辑调用上下文的项必须是可序列化的。对于包含了逻辑调用上下文数据项的执行上下文,让它流动起来可能严重损害性能,因为为了捕捉执行上下文,需要对所有数据项进行序列化和反序列化。

标签: C#

Powered by emlog  蜀ICP备18021003号-1   sitemap

川公网安备 51019002001593号