命名空间:System.Runtime.Remoting.Messaging

类型完全限定名称:System.Runtime.Remoting.Messaging.CallContext

官方介绍:https://docs.microsoft.com/zh-cn/dotnet/api/system.runtime.remoting.messaging.callcontext?redirectedfrom=MSDN&view=netframework-4.8

用途:

  用于提供与执行代码路径一起传送的属性集,直白讲就是:提供线程(多线程/单线程)代码执行路径中数据传递的能力。

方法 描述 是否可用于多线程环境
SetData 存储给定的对象并将其与指定名称关联。
GetData 从System.Runtime.Remoting.Messaging.CallContext中检索具有指定名称的对象
LogicalSetData 将给定的对象存储在逻辑调用上下文,并将其与指定名称关联。
LogicalGetData  从逻辑调用上下文中检索具有指定名称的对象。
FreeNamedDataSlot 清空具有指定名称的数据槽。
HostContext  获取或设置与当前线程相关联的主机上下文。在Web环境下等于System.Web.HttpContext.Current

为了更加明确的认识这些方法的作用以及作用我们通过以下代码来了解:

首先定义一个类:

public class User
{
  public string Id { get; set; }

  public string Name { get; set; }
}

一、单线/多线程环境,测试GetData、SetData、FreeNamedDataSlot

下边的输出分别对应1、2、3的输出

根据上述测试结果我们基本可以得出以下结论:

1、GetData、SetData只能用于单线程环境,如果发生了线程切换,存储的数据也会随之丢失

2、可以用于同一线程中的不同地方,传递数据

一、单线/多线程环境,测试LogicalSetData、LogicalGetData、FreeNamedDataSlot

通过上述测试可得出结论:

a、FreeNamedDataSlot只能清除当前线程的数据槽,不能清除子线程的数据槽;

b、LogicalSetData、LogicalGetData可用于在多线程环境下传递数据;

c、FreeNamedDataSlot清除当前线程,之前已经运行子任务,不受影响

通过与上一测试对比我们可以得出结论:

a、FreeNamedDataSlot只能清除当前线程的数据槽

b、LogicalSetData只是存储当前线程以及子线程的数据槽

c、LogicalGetData获取的是当前线程或父线程的数据槽对象,拿到的时对象的引用

d、子线程中使用LogicalSetData改变数据槽的值,不能印象父线程的数据槽,及时他们的key时一个

现在清楚了它的功能,这样我们就可以来思考它的使用场景。

1、可以解耦代码,以前我们向下传递数据是通过参数变量的形式,方法嵌套,我么就需要额外的参数一层一层传递。如果我们Get了CallContext技能,就可以对代码进行解耦;

2、工作单元(UOW),像XPO的工作单元以及ABP的工作单元,都是通过CallContext来实现的,ABP详见IUnitOfWorkManager.Current->ICurrentUnitOfWorkProvider.Current->CallContextCurrentUnitOfWorkProvider.Current

3、 System.Web.HttpContext.Current我们常用来获取当前请求上下文,使用的是System.Runtime.Remoting.Messaging.CallContext.HostContext

System.Runtime.Remoting.Messaging.CallContext.HostContext get访问器和set访问器实现和GetData和SetData的实现方式一致

在.NET Core中使用AsyncLocal代替CallContext,实现的是CallContext.LogicalGetData 和CallContext.SetLogicalCallContext

此处需要重点说下ILogicalThreadAffinative,其作用是:将一个对象,可以将外部传播标记 System.AppDomain 中 System.Runtime.Remoting.Messaging.LogicalCallContext。

如果需要存在在执行上下文中的对象继承了ILogicalThreadAffinative接口,使用LogicalSetData/LogicalGetData和SetData/GetData作用是一样的。请看以下源码截图:

     [SecurityCritical]
public static void SetData(string name, object data)
{
if (data is ILogicalThreadAffinative)
{
LogicalSetData(name, data);
}
else
{
ExecutionContext mutableExecutionContext = Thread.CurrentThread.GetMutableExecutionContext();
mutableExecutionContext.LogicalCallContext.FreeNamedDataSlot(name);
mutableExecutionContext.IllogicalCallContext.SetData(name, data);
}
}
        [SecurityCritical]
public static object GetData(string name)
{
object obj2 = LogicalGetData(name);
return ((obj2 != null) ? obj2 : IllogicalGetData(name));
}

我们可以看到,不管你是使用LogicalSetData还是SetData存储的数据,我们使用GetData都是可以取到的。如果SetData的值是继承ILogicalThreadAffinative接口,那不管使用那个都是一样的效果;

实际上在使用System.Web.HttpContext.Current是通过CallContext.HostContext实现的,在看其源码时发现一个很不理解的现象

     public static object HostContext
{
[SecurityCritical]
get
{
ExecutionContext.Reader executionContextReader = Thread.CurrentThread.GetExecutionContextReader();
object hostContext = executionContextReader.IllogicalCallContext.HostContext;
if (hostContext == null)
{
hostContext = executionContextReader.LogicalCallContext.HostContext;//这里又为什么取,不是无用功么
}
return hostContext;
}
[SecurityCritical]
set
{
ExecutionContext mutableExecutionContext = Thread.CurrentThread.GetMutableExecutionContext();
if (value is ILogicalThreadAffinative)
{
mutableExecutionContext.IllogicalCallContext.HostContext = null;
mutableExecutionContext.LogicalCallContext.HostContext = value;
}
else
{
mutableExecutionContext.IllogicalCallContext.HostContext = value;
mutableExecutionContext.LogicalCallContext.HostContext = null;//此处为什么要设置为null
}
}
}

仔细看了下,取决于

ILogicalThreadAffinative,要不要传播,以此减少一次存储。

.NET多线程之调用上下文CallContext的更多相关文章

  1. CallContext线程数据缓存-调用上下文

    一.CallContext 概述 命名空间:System.Runtime.Remoting.Messaging CallContext 用于提供与执行代码路径一起传送的属性集,直白讲就是:提供线程(多 ...

  2. .NET:线程本地存储、调用上下文、逻辑调用上下文

    .NET:线程本地存储.调用上下文.逻辑调用上下文 目录 背景线程本地存储调用上下文逻辑调用上下文备注 背景返回目录 在多线程环境,如果需要将实例的生命周期控制在某个操作的执行期间,该如何设计?经典的 ...

  3. C# 线程本地存储 调用上下文 逻辑调用上下文

    线程本地存储 using System; using System.Threading; using System.Threading.Tasks; namespace ConsoleAppTest ...

  4. 如何在多线程中调用winform窗体控件

    由于 Windows 窗体控件本质上不是线程安全的.因此如果有两个或多个线程适度操作某一控件的状态(set value),则可能会迫使该控件进入一种不一致的状态.还可能出现其他与线程相关的 bug,包 ...

  5. Delphi 多线程 “尚未调用CoInitialize错误”的解决方法

    在Delphi  多线程中出现“尚未调用CoInitialize错误”的解决方法 解决方法如下: function  TMyThread.ExecTimer: Boolean;begin  Resul ...

  6. HttpContext在多线程异步调用中的使用方案

    1.在线程调用中,有时候会碰到操作文件之类的功能.对于开发人员来说,他们并不知道网站会被部署在服务器的那个角落里面,因此根本无法确定真实的物理路径(当然可以使用配置文件来配置物理路径),他们唯一知道的 ...

  7. 使用Condition实现多线程之间调用(生产消费模式)

    一,object 类的wait(),notify()和notifyAll() Java 线程类也是一个object 类,它的实例都继承自java.lang.Thread 或其子类.wait(),not ...

  8. JavaScript调用上下文(第九天)

    call与apply用法 使用哪个对象去调用相应的方法: var name="window"; var obj={ name:"obj" } function ...

  9. C++多线程中调用python api函数

    错误场景:一直等待全局锁. 解决方法: 一.首先定义一个封装类,主要是保证PyGILState_Ensure, PyGILState_Release配对使用,而且这个类是可以嵌套使用的. #inclu ...

随机推荐

  1. WPF MVVM笔记

    学习了一番WPF MVVM,记录一下,微软的连接(https://msdn.microsoft.com/zh-cn/magazine/dd419663.aspx) 1.mvvm就是model,view ...

  2. css3 pointer-events 让对象如透明般直接响应下层对象的鼠标事件

    引用:http://www.css88.com/book/css/properties/user-interface/pointer-events.htm 语法: pointer-events:aut ...

  3. HDFS的几点改进

    HDFS(Hadoop Distributed File System)是一个运行在商用机器上面的分布式文件系统,其设计思想来自于google著名的Google File System论文. HDFS ...

  4. Linux基础命令杂记

    今天又一次搞Linux生产环境搭建.这是种步骤很多,很繁琐而且又不得不做的事情.虽然做过很多次,但还是有很多步骤.命令不记得,每一次到处找资料很麻烦,于是将一些步骤记下,以便查找. 登录远程MySQL ...

  5. Qt使用windows API获取程序运行时占用内存 good

    使用的是psapi.h中的GetProcessMemoryInfo函数,但是运行到该函数时就强制退出了. 后来,百度到原因是 原来Qt编译时加了-mthread,createprocess时要使的Ha ...

  6. CentOS7中firewalld的安装与使用详解

    一.软件环境 [root@Geeklp201 ~]# cat /etc/redhat-release CentOS Linux release 7.4.1708 (Core) 二.安装firewall ...

  7. 利用批处理自动创建schtasks系统任务

    通过批处理自动创建schtasks系统任务,把下列代码保存成bat文件,放到要执行的文件的同级目录即可. @echo on set curpath=%cd%c:cd %systemroot%schta ...

  8. # 构建以及运行Springboot Docker镜像时的变量传递

    Docker可以把我们的运行环境打包,然后我们只要run就可以了.大部分hello world都是这么写的.但都缺少了实际应用环节.以springboot为例,hello world的Dockerfi ...

  9. Android native进程间通信实例-binder篇之——用parcel传输数组

     和之前稍微不同,这次要稍微分析一下 Parce.cpp 和 android_os_Parcel.cp p的源码,为的是能够掌握调试技巧,后续传输其它类型数据就能举一反三了!   1. 代码共享 这次 ...

  10. Vue.js 面试题整理

    Vue项目结构介绍 build 文件夹:用于存放 webpack 相关配置和脚本. config 文件夹:主要存放配置文件,比如配置开发环境的端口号.开启热加载或开启gzip压缩等. dist 文件夹 ...