命名空间: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. 破处在window7桌面版本下golang连接数上限

    注册表 HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters 修改或者添加 MaxUserPort 60000 TcpTimedWaitDel ...

  2. PE格式大图

  3. 零元学Expression Blend 4 - Chapter 46 三分钟快速充电-设定Margin的小撇步

    原文:零元学Expression Blend 4 - Chapter 46 三分钟快速充电-设定Margin的小撇步 如果需要经常的使用某一项工具,总会希望能够更快速的使用各项设定达到效果 今天要介绍 ...

  4. 基于X.509证书和SSL协议的身份认证过程实现(OpenSSL可以自己产生证书,有TCP通过SSL进行实际安全通讯的实际编程代码)good

    上周帮一个童鞋做一个数字认证的实验,要求是编程实现一个基于X.509证书认证的过程,唉!可怜我那点薄弱的计算机网络安全的知识啊!只得恶补一下了. 首先来看看什么是X.509.所谓X.509其实是一种非 ...

  5. [转] Protobuf高效结构化数据存储格式

    从公司的项目源码中看到了这个东西,觉得挺好用的,写篇博客做下小总结.下面的操作以C++为编程语言,protoc的版本为libprotoc 3.2.0. 一.Protobuf? 1. 是什么?  Goo ...

  6. Tomcat请求过程

    Tomcat请求过程 1.用户点击网页内容,请求被发送到本机端口8080,被在那里监听的Coyote HTTP/1.1 Connector获得. 2.Connector把该请求交给它所在的Servic ...

  7. Delphi中动态调用TXMLDocument的经历

    var  vXMLDocument: TXMLDocument;begin  vXMLDocument := TXMLDocument.Create('c:/temp/temp.xml');  Cap ...

  8. Linux用户和权限

    用户和用户组管理 linux使用用户和组来限制资源的使用. 在linux上运行任何程序或命令都要以一个具体的用户来运行,这个用户的权限决定了相应的程序能访问的资源和能执行的操作. 用户相关命令 组是用 ...

  9. sql 日志恢复

    可能有不少朋友遇到过这样的问题: update或delete语句忘带了where子句,或where子句精度不够,执行之后造成了严重的后果,这种情况的数据恢复只能利用事务日志的备份来进行,所以如果你的S ...

  10. hadoop之hive基本操作

    -- 清空表中的数据,保留表结构 truncate table tmp_userid; '); -- 搜索库或表支持正则表达式 show tables 'sa*'; show tables in ba ...