CallContext线程数据缓存-调用上下文
一、CallContext 概述
命名空间:System.Runtime.Remoting.Messaging
CallContext 用于提供与执行代码路径一起传送的属性集,直白讲就是:提供线程(多线程/单线程)代码执行路径中数据传递的能力。
当对另一个 AppDomain 中的对象进行远程方法调用时,CallContext 类将生成一个与该远程调用一起传播的 LogicalCallContext 实例。只有公开 ILogicalThreadAffinative 接口并存储在 CallContext 中的对象被在 LogicalCallContext 中传播到 AppDomain 外部。
CallContext成员
- SetData: 存储给定的对象并将其与指定名称关联。
- GetData: 从CallContext中检索具有指定名称的对象
- LogicalSetData: 将给定的对象存储在逻辑调用上下文,并将其与指定名称关联。可用于多线程环境
- LogicalGetData: 从逻辑调用上下文中检索具有指定名称的对象。可用于多线程环境
- FreeNamedDataSlot: 清空具有指定名称的数据槽。可用于多线程环境
- HostContext属性: 获取或设置与当前线程相关联的主机上下文。在Web环境下等于System.Web.HttpContext.Current
GetData、SetData
只能用于单线程环境,如果发生了线程切换,存储的数据也会随之丢失
可以用于同一线程中的不同地方,传递数据
LogicalSetData、LogicalGetData
- LogicalSetData、LogicalGetData可用于在多线程环境下传递数据;
- LogicalSetData只是存储当前线程以及子线程的数据槽
- LogicalGetData获取的是当前线程或父线程的数据槽对象,拿到的是对象的引用
- FreeNamedDataSlot清除当前线程,之前已经运行子任务,不受影响,不能清除子线程的数据槽;
二、 CallContext不跨线程传播的方法:GetData、SetData
可以利用CallContext 实现单例,默认情况下,CallContext 的数据不跨线程传播。
1、在处理多组件共用Context时非常有用,比如常见的EF 可以将实例的DBEntity存储在其中,可以一次访问只实例化一次,便于管理且不用多次实例访问对象
public static class DbContextHelper
{
private static DbContext context = null;
private const string SessionKey_DbContext = "Entities";
public static DbContext GetDbContext()
{
if (CallContext.GetData(SessionKey_DbContext) == null)
{
CallContext.SetData(SessionKey_DbContext, new Entities());
}
return CallContext.GetData(SessionKey_DbContext) as Entities;
}
}
2、类单例
void Main()
{
MyAppContext.Current.FirstName = "a";
Console.Write(MyAppContext.Current.FirstName);
} public class MyAppContext
{
const string contextKey = "MyAppContext:ContextKey";
public string FirstName { get; set; }
public static MyAppContext Current
{
get
{
if (CallContext.GetData(contextKey) == null)
{
CallContext.SetData(contextKey, new MyAppContext());
}
return CallContext.GetData(SessionKey_DbContext) as MyAppContext;
}
}
}
三、 CallContext跨线程传播的方法:ILogicalSetData、LogicalGetData
要让CallContext实现跨线程传播,可以调用CallContext的静态方法ILogicalSetData,或让上下文类实现ILogicalThreadAffinative 接口。
线程本地存储
线程池可能不会释放使用过的线程,导致多次执行之间可能共享数据(可以每次执行前重置线程本地存储的数据)。
for (var i = 0; i < 10; i++)
{
Thread.Sleep(10); Task.Run(() =>
{
var slot = Thread.GetNamedDataSlot("test");
if (slot == null)
{
Thread.AllocateNamedDataSlot("test");
} if (Thread.GetData(slot) == null)
{
Thread.SetData(slot, DateTime.Now.Millisecond);
} Console.WriteLine(Thread.CurrentThread.ManagedThreadId + ":" + Thread.GetData(slot));
});
}
结果

调用上下文
每次执行的数据是完全隔离的,非常符合我们的期望。但是,如果我们期望调用期间又开启了一个子线程,如何让子线程访问父线程的数据呢?这就需要使用到:“逻辑调用上下文”。
Console.WriteLine("测试:CallContext.SetData");
for (var i = 0; i < 10; i++)
{
Thread.Sleep(10);
Task.Run(() =>
{
if (CallContext.GetData("test") == null)
{
CallContext.SetData("test", DateTime.Now.Millisecond);
}
Console.WriteLine(Thread.CurrentThread.ManagedThreadId + ":" + CallContext.GetData("test"));
});
}
结果
每次执行的数据是完全隔离的,非常符合我们的期望。
逻辑调用上下文
如果我们期望调用期间又开启了一个子线程,如何让子线程访问父线程的数据呢?这就需要使用到:“逻辑调用上下文”。
注意 ExecutionContext.SuppressFlow(); 和ExecutionContext.RestoreFlow();它们分别能阻止传播和重置传播,默认是允许传播的。
Console.WriteLine("测试:CallContext.SetData");
Task.Run(() =>
{
CallContext.SetData("test", "段光伟");
Console.WriteLine(Thread.CurrentThread.ManagedThreadId + ":" + CallContext.GetData("test"));
Task.Run(() =>
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId + ":" + CallContext.GetData("test"));
});
});
Thread.Sleep(100);
Console.WriteLine("测试:CallContext.LogicalSetData");
Task.Run(() =>
{
CallContext.LogicalSetData("test", "段光伟");
Console.WriteLine(Thread.CurrentThread.ManagedThreadId + ":" + CallContext.LogicalGetData("test"));
Task.Run(() =>
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId + ":" + CallContext.LogicalGetData("test"));
});
ExecutionContext.SuppressFlow();
Task.Run(() =>
{
Console.WriteLine("SuppressFlow 之后:" + CallContext.LogicalGetData("test"));
});
ExecutionContext.RestoreFlow();
Task.Run(() =>
{
Console.WriteLine("RestoreFlow 之后:" + CallContext.LogicalGetData("test"));
});
});
输出

四、Web中的CallContext
HttpContext.Current(包括Session)的存储是基于当前线程的CallContext,在非请求处理线程(即其他线程)是无法获取当前HttpContext的(不跨线程传播)。
CallContext线程数据缓存-调用上下文的更多相关文章
- C# 线程本地存储 调用上下文 逻辑调用上下文
线程本地存储 using System; using System.Threading; using System.Threading.Tasks; namespace ConsoleAppTest ...
- .NET:线程本地存储、调用上下文、逻辑调用上下文
.NET:线程本地存储.调用上下文.逻辑调用上下文 目录 背景线程本地存储调用上下文逻辑调用上下文备注 背景返回目录 在多线程环境,如果需要将实例的生命周期控制在某个操作的执行期间,该如何设计?经典的 ...
- .NET多线程之调用上下文CallContext
命名空间:System.Runtime.Remoting.Messaging 类型完全限定名称:System.Runtime.Remoting.Messaging.CallContext 官方介绍:h ...
- Android消息机制之实现两个不同线程之间相互传递数据相互调用
目的:实现两个不同线程之间相互传递数据相互调用方法. 线程一中定义mainHandler 并定义一个方法mainDecode 线程二中定义twoHandler 并定义一个方法twoEncode 实现当 ...
- Memcached 数据缓存系统
Memcached 数据缓存系统 常用命令及使用:http://www.cnblogs.com/wayne173/p/5652034.html Memcached是一个自由开源的,高性能,分布式内存对 ...
- jQuery源代码学习之六——jQuery数据缓存Data
一.jQuery数据缓存基本原理 jQuery数据缓存就两个全局Data对象,data_user以及data_priv; 这两个对象分别用于缓存用户自定义数据和内部数据: 以data_user为例,所 ...
- LINQ-to-SQL那点事~LINQ-to-SQL中的数据缓存与应对
回到目录 这个文章写的有点滞后了,呵呵,因为总想把之前不确定的东西确定了之后,再写这篇,之前的LINQ-to-SQL那点事,请点这里. LINQ-to-SQL中的数据缓存与应对 Linq-to-SQL ...
- Android开发——获取应用数据/缓存大小并清理缓存
1. 获取应用数据/缓存大小 其中pm为实例化的PackageManager,因为需要遍历所有的已安装的应用.因此需要开启子线程进行处理. 还有需要注意的是,在Android4.2之前getPacka ...
- jQuery对象数据缓存Cache原理及jQuery.data详解
网上有很多教你怎么使用jQuery.data(..)来实现数据缓存,但有两个用户经常使用的data([key],[value])和jQuery.data(element,[key],[value])几 ...
随机推荐
- ELK实战搭建+x-pack安全认证
阅读目录: ELK日志平台入门简介1.1 ELK原理拓扑图1.2 Elasticsearch安装配置1.3 Kibana安装配置1.4 Kibana汉化及时区修改1.5 Logst ...
- 冲刺Noip2017模拟赛8 解题报告——五十岚芒果酱
1.鼎纹 [问题描述] 据说鼎纹的 种制造 式是 铜模印出来的,这是我国古代劳动 智慧 的结晶.铜模印过的地 ,会留下深深的印记,经过时间的炼化,洗 练成历史的遗存. 聪明的古代劳动人民拥有一个 a ...
- tomcat修改server.xml的虚拟目录,启动eclipse后清空
修改eclipse中servers的server.xml 参考文章:https://blog.csdn.net/binglong_world/article/details/77604324
- python return逻辑判断表达式(21)
一.return逻辑判断表达式 and and:遇假则假,所以前面为假就不执行和判断后面直接返回假:前面为真则继续判断执行后面直到表达式结束或者出现假为止; # !usr/bin/env python ...
- CCF201403 无线网络【限制型最短路】
问题描述 目前在一个很大的平面房间里有 n 个无线路由器,每个无线路由器都固定在某个点上.任何两个无线路由器只要距离不超过 r 就能互相建立网络连接. 除此以外,另有 m 个可以摆放无线路由器的位置. ...
- 点亮文字(CSS)
html <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8& ...
- 封装Json+日志
/** * 输出json * @param $msg * @param int $errno */ public function printOutError($msg = '操作失败', $errn ...
- 《Mysql 锁 - 概述》
一:锁类型(加锁范围区分类型) - MySQL里面的锁可以分为:全局锁.表级锁.行级锁. 二:全局锁 - 作用 - 对整个数据库实例加锁. - 加锁方式 - MySQL提供加全局读锁的方法:Flus ...
- LIUNX随堂学习-2 用户和组,增、删、改、查
1.less的使用 less /etc/group #查看组的信息 less /etc/gshadow #查看组密码 例如:less /tmp/aa ...
- 方法引用(method reference)
目录 方法引用(method reference) 1. 含义 2. 分类 3. 总结 方法引用(method reference) 1. 含义 方法引用实际上是 Lambda 表达式的一种语法糖. ...