前一段时间正好要在某个网页程序上开一个多线程调用多个组件的尝试,这些组件是有其他团队开发的(如:印度/俄罗斯),所以修改它们的代码看起来是不太现实的,但是,令人恼火的是他们的代码中大量的用到了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对象,带来的副作用可能要比想象的大得多。

转载于:https://www.cnblogs.com/vwxyzh/archive/2009/02/21/1395416.html

CallContext和多线程相关推荐

  1. Abp Uow 设计

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

  2. .NET | 多线程下的调用上下文 : CallContext

    [.NET]| 总结/Edison Zhou 最近在分析现在团队的项目代码(基于.NET Framework 4.5),经常发现一个CallContext的调用,记得多年前的时候用到了它,但是印象已经 ...

  3. c# 多线程单例模式_单例模式,多线程单例,双重锁定单例,工场单例创建上下文...

    单例模式,多线程单例,双重锁定单例,工厂单例创建上下文. 单例子模式定义 保证一个类仅有一个实例,并提供一个访问它的全局访问点. 通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个 ...

  4. Java 多线程概述

    多线程技术概述 1.线程与进程 进程:内存中运行的应用程序,每个进程都拥有一个独立的内存空间. 线程:是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换.并发执行,一个进程最少有一个线程, ...

  5. Java 多线程的基本方式

    Java 多线程的基本方式 基础实现两种方式: 通过实现Callable 接口方式(可得到返回值):

  6. RPC 笔记(08)— socket 通信(多进程多线程服务器)

    在上一节中如果并行的客户端连接数超过了默认开启进程的数量,那么后来的客户端请求将会阻塞,为了不阻塞新的客户端,我们可以将进程的单线程改成多线程即可. ​ 服务端代码: import json impo ...

  7. Python 多线程总结(2)— 线程锁、线程池、线程数量、互斥锁、死锁、线程同步

    主要介绍使用 threading 模块创建线程的 3 种方式,分别为: 创建 Thread 实例函数 创建 Thread 实例可调用的类对象 使用 Thread 派生子类的方式 多线程是提高效率的一种 ...

  8. Python 多线程总结(1)- thread 模块

    thread 模块 1. 单线程 首先看下单线程程序运行的例子,如下所示, import timedef loop0():print 'start loop0 begin', time.ctime() ...

  9. Python多线程调试

    有时候程序是多线程的,调试的时候可能跑到别的线程了. 这个时候把thread.start变成threa.run就好了,就会执行完当前线程再执行下一个. for thread in threads:th ...

最新文章

  1. linux message日志只有4k,命令长期运行 常用技巧 Linux 服务器 · 404k的前后端日志...
  2. 美国独步世界的八大领域
  3. 把整个DIV变成超链接
  4. 高等学校精品规划教材 计算机专业英语,计算机专业英语
  5. 将客户端图片保存到数据库中的方法
  6. python画完图 程序暂停运行_Python编程:认识IDLE,编写保存运行第1个程序
  7. Golang 汇编入门知识总结
  8. linux监测node进程,通过node_exporter监控linux服务器一
  9. java泛型不是计算运行时的数据类型
  10. 处理sharepoint 列表中的 person or group类型字段
  11. 什么是Prettier?
  12. 1-7 Graph 可视化
  13. cefsharp 二次开发
  14. 素描构图中的对比与调和
  15. 怎么制作GIF高清动态表情包
  16. 这些优秀的音视频开源框架你值得收藏
  17. JAVA Swing + Jdbc 实现宿舍管理系统
  18. gethostbyname, gethostbyaddr(原来百度叫shifen 十分?)
  19. Tomcat 配置详解/优化方案
  20. 配置Anaconda中Jupyter Notebook的默认启动目录

热门文章

  1. LiveGBS无插件播放页面的集成----单独的播放器样式
  2. 开发中遇到的问题,以及笔记
  3. 机器学习(五) 关于散点图生成
  4. 今天下棋,结合以前一些技巧的总结
  5. 备份MySQL数据库
  6. Android 用户界面---定制组件(Custom Components)
  7. 用Aspose.Words for .NET动态生成word文档中的图片或水印
  8. beta book读书俱乐部的构思
  9. 系统集成项目管理工程师-变更管理笔记
  10. hdu1960 最小路径覆盖