前一段时间正好要在某个网页程序上开一个多线程调用多个组件的尝试,这些组件是有其他团队开发的(如:印度/俄罗斯),所以修改它们的代码看起来是不太现实的,但是,令人恼火的是他们的代码中大量的用到了AppContext.Current这个对象(实际上是用了HttpContext.Current.Item来存储的),而一旦异步,HttpContext.Current就不复存在,自然就会不停的报出空引用异常,看起来异步是不太现实的了。

  就在无计可施的时候,突然发现有一个叫CallContext的奇怪的类,藏匿在System.Runtime.Remoting.Messaging这个几乎没人用的namespace下面,当然一开始我仅仅是被它的名称所吸引,直译过来不就是调用上下文吗?感觉这个东西能有点作用。于是查阅了msdn,描述如下:

提供与执行代码路径一起传送的属性集。无法继承此类。

  一句废话。。。看备注吧:

CallContext 是类似于方法调用的线程本地存储区的专用集合对象,并提供对每个逻辑执行线程都唯一的数据槽。数据槽不在其他逻辑线程上的调用上下文之间共享。当 CallContext 沿执行代码路径往返传播并且由该路径中的各个对象检查时,可将对象添加到其中。

当对另一个 AppDomain 中的对象进行远程方法调用时,CallContext 类将生成一个与该远程调用一起传播的 LogicalCallContext 实例。只有公开 ILogicalThreadAffinative 接口并存储在CallContext 中的对象被在 LogicalCallContext 中传播到 AppDomain 外部。不支持此接口的对象不在 LogicalCallContext 实例中与远程方法调用一起传输

  似乎有那么点意思,不过逻辑线程的定义似乎有那么点模棱两可,不过,也提到了一个接口ILogicalThreadAffinative,再看看这个接口定义了什么,一看成员定义。。。没有成员。。。一个空接口,汗了一把,还是看看msdn上如何描述的吧:

标记可以在 LogicalCallContext 中传播到 AppDomain 外部的对象。

  强调了在remoting中的作用,再看看备注:

当对另一个 AppDomain 中的对象进行远程方法调用时,当前的 CallContext 类生成一个将与该调用一起传播到远程位置的 LogicalCallContext。只有公开 ILogicalThreadAffinative 接口并存储在 CallContext 中的对象被传播到 AppDomain 外部。不支持此接口的对象不在LogicalCallContext 实例中与远程方法调用一起传输。

  也是强调在remoting中的作用,但是可以想象,基本上是CallContext中用了类似is ILogicalThreadAffinative的方式,来区别对待不同的对象,对于符合这个接口的将被放到LogicalCallContext,而不符合的另外处理。

  从文档角度,似乎已经没有什么进展了,这时候,突然想起来一个以前看过的很有趣的类型ExecutionContext,namespace是System.Threading,看起来就是多线程准备的,不过,msdn上的例子就说了如何控制传递权限对象的问题,并没有说到如何传递普通对象。

  一时想到所谓的LogicalCallContext会不会在ExecutionContext中存在哪?查了一下msdn,看到备注:

ExecutionContext 类为与执行的逻辑线程相关的所有信息提供单个容器。这包括安全上下文、调用上下文和同步上下文。

ExecutionContext 类提供的功能让户代码可以在用户定义的异步点之间捕获和传输此上下文。公共语言运行库确保在托管进程内运行库定义的异步点之间一致地传输 ExecutionContext。

执行上下文是 COM 单元的托管等效项。在应用程序域中,每当传输线程时都必须传输整个执行上下文。在由 Thread..::.Start 方法、大多数线程池操作和通过 Windows 消息泵进行的 Windows 窗体线程封送处理所导致的传输过程中,将会出现这种情况。在不安全的线程池操作(如UnsafeQueueUserWorkItem 方法)中不会出现这种情况,原因是不安全的线程池操作不会传输压缩堆栈。每当压缩堆栈流动时,托管的主体、同步、区域设置和用户上下文也随之流动。ExecutionContext 类提供 Capture 和 CreateCopy 方法以获取执行上下文,并提供 Run 方法以设置当前线程的执行上下文。

与某个线程相关联的 ExecutionContext 无法在另一个线程上进行设置。尝试这样做会导致引发异常。若要将 ExecutionContext 从一个线程传播到另一个线程,请制作 ExecutionContext 的副本。

ExecutionContext 在内部存储与 LogicalCallContext 相关联的所有数据。这使得可以在复制和传输 ExecutionContext 时传播 LogicalCallContext 数据。

  果然,ExecutionContext中有LogicalCallContext的数据,并且很好的说明了,无论是Thread.Start还是用线程池大多数操作,ExecutionContext都会自动将这些数据传递给那些线程(关于ThreadPool.UnsafeQueueUerWorkItem方法相信用的人应该不多),看起来演员们都到齐了,马上可以演出一场多线程的好戏了。

  首先是起着关键作用ExectionContext,也许我们的代码中没必要出现它的身影,但是那仅仅是因为.net类库的方法,为我们很好封装了这个功能,没有它,想在一个线程中把一个对象告诉另一个线程,就只有通过堆了。

  其次,LogicalCallContext,在众多ExecutionContext传播的对象中,很多是我们无法简单的利用的(总不能为了传播一个对象,去定义一个自定义的权限吧),而LogicalCallContext就是自定义对象的最好的载体。

  最后,剩下的问题就是如何读写这个LogicalCallContext的问题了,也就是终于轮到CallContext出场了。看一下CallContext为我们准备了些什么方法:GetData, SetData, LogicalGetData, LogicalSetData, FreeNamedDataSlot, GetHeader, SetHeader以及HostContext属性。

  第一焦点,当然是GetData和SetData这两个方法,做了个简单的测试,发现这两个方法,确实就是通过ILogicalThreadAffinative接口来决定是否要把对象发给新线程的,而删除这个数据的方法就是FreeNamedDataSlot,现在只要为每一个要在多线程中共用的对象加上一个空接口,并且在多线程开始前在主线程中把对象设置进去,然后在其他线程中再取出来就可以把问题搞定了。多线程部分结束后,不要忘记用FreeNamedDataSlot去删除一下。

  不知道大家注意到没有,出了GetData和SetData外,还有一对LogicalGetData和LogicalSetData,这两个是干什么用的哪?是不是和LogicalCallContext的Logical有什么关系哪?

  又把前面的那个简单的实验做了一下,只不过用了LogicalGetData和LogicalSetData这两个方法,结论是无论是否实现ILogicalThreadAffinative接口,对象都可以在新线程内被访问到,也就是说现在可以传播任何数据给新线程,包括.net定义的string,int等基础类型,这个方案已经接近完美了。

  回过头来看看我的任务吧,只需要修改AppContext.Current属性的实现,并且在多线程开始之前和之后做一个小小的处理,其他的组件就可以原封不动的并发的跑在各自的线程上。

  工作上的事情就到此为止了。

  在来说说CallContext.HostContext属性是干什么的,经过简单的测试,发现在Asp.net程序中,这个HostContext里面放的就是HttpContext的实例,ms也够偷懒的。其实ms只要做一个很小的修改,Asp.net的多线程就不用这么麻烦了,只需要HttpContext实现一下ILogicalThreadAffinative接口,无论新开多少个线程,到那边都能访问到HttpContext.Current了,当然ms没有这么做也是有原因的,一旦HttpContext被传播到其他线程,那么asp.net就很难控制HttpContext对象的生命周期,而HttpContext对象又引用这HttpRequest对象,带来的副作用可能要比想象的大得多。

CallContext和多线程的更多相关文章

  1. Asp.Net在多线程环境下的状态存储问题

    在应用开发中,我们经常需要设置一些上下文(Context)信息,这些上下文信息一般基于当前的会话(Session),比如当前登录用户的个人信息:或者基于当前方法调用栈,比如在同一个调用中涉及的多个层次 ...

  2. Abp Uow 设计

    初始化入口 在AbpKernelModule类中,通过UnitOfWorkRegistrar.Initialize(IocManager) 方法去初始化 /// <summary> /// ...

  3. CallContext的LogicalCallContext在多线程环境下面公用变量

    压根名听说过这个类的看这里:如何实现对上下文(Context)数据的统一管理 原来以为CallContext就可以直接在多线程环境下面共享使用的,今天突然想到:Asp.Net环境下面,设置来设置去的, ...

  4. .NET多线程之调用上下文CallContext

    命名空间:System.Runtime.Remoting.Messaging 类型完全限定名称:System.Runtime.Remoting.Messaging.CallContext 官方介绍:h ...

  5. 快速入门系列--CLR--02多线程

    最近,由于基础框架的整体升级,因此需要更新所有相关项目的DLL文件.这个过程存在不小的风险,因此也对发布后的生产服务器进行了密切的监控,结果还是出现了个别应用出现异常的情况,很快的占用了大量的服务器内 ...

  6. 多线程要点--CLR C#学习笔记

    1.windows永远不会调度一个进程,只调度线程. 2.线程和操作系统的关系:CLR(X)--AppDomain--线程池(包含工作者线程和I/O线程) 3.线程的关键组成部分 A.线程执行上下文 ...

  7. ibatis.net 多线程的调试

    ibatis是一个挺不错的半自动orm框架,从java移植到c# 在ibatis中是支持多线程操作的,但是这几天的使用过程中发现就框架本身任然存在一些问题,可能会让你对多线程的使用并不是那么的顺利 在 ...

  8. ASP.NET多线程下使用HttpContext.Current为null解决方案

    多线程或者异步调用中如何访问HttpContext? 前面我还提到在APM模式下的异步完成回调时,访问HttpContext.Current也会返回null,那么此时该怎么办呢? 答案有二种:1. 在 ...

  9. 关于callContext

    coding们肯定有这种需求,在程序中,方法一级级调下去,比如A->b->C->D.... ->Z.在调用过程中,希望在调用函数之间传递一些数据,常见的是将特定的数据从高往低处 ...

随机推荐

  1. WPF 设置WebBrowser控件不弹脚本错误提示框

    using System.Reflection; using System.Windows; using System.Windows.Controls; using System.Windows.N ...

  2. C#之自己定义的implicit和explicit转换

    在类型转换时常会遇到隐式转换和显式转换.那我们自己定义的类型要怎样去定义隐式转换和显式转换?我们来看一段代码 public class Rational { private Int32 _inner_ ...

  3. Caché Monitor 2.03发布,Caché的SQL开发工具 - 开源中国社区

    Caché Monitor 2.03发布,Caché的SQL开发工具 - 开源中国社区 Caché Monitor 2.03发布,Caché的SQL开发工具

  4. Linux Shell脚本编程--curl命令详解

    用途说明 curl命令是一个功能强大的网络工具,它能够通过http.ftp等方式下载文件,也能够上传文件.其实curl远不止前面所说的那些功能,大家可以通过man curl阅读手册页获取更多的信息.类 ...

  5. Managing Data in Containers

    Managing Data in Containers So far we've been introduced to some basic Docker concepts, seen how to ...

  6. 【Android小应用】颈椎保健操Android开源项目

    前段时间在知乎上回答已入 IT 行业的前辈,有哪些关于保护身体健康的知识分享给 IT 新人? ,回复了一张图片,评论里面有知友希望通过程序可以实现,我后面尝试着通过程序实现了效果,现开源出来,大家可以 ...

  7. BGP的状态机制

    Idle 状态:即空闲状态,不接受任何BGP的连接,等待Start事件的产生,如果有start事件产生,若有start事件产生,系统开启ConnectRetry定时器,向邻居发起TCP连接,并将状态变 ...

  8. Linux智能小开关rfkill

    Linux智能小开关rfkill Rfkill,当中rf是Radio frequency(射频).主要作用是一个专门管理开关的子系统,举例说明Android手机的通知栏能够方便地开关Airplane/ ...

  9. MySQL命令行数据操作使用心得(总结版)

    Char 0~255 Varchar 0~65535 text 0~65535(只能保存字符) Longtext 0~4294967295(只能保存字符) CMD登陆mysql mysql -u ro ...

  10. wordpress博客近期变慢之解决(fonts.google.com)

    近期发现站点訪问速度变慢.博客文章打开速度特慢,也没改动过东西. 并且近期发现google的服务非常多訪问都打不开或是变慢. 于是知道可能是那"伟大东西"在作坏事了. 症状: 网页 ...