我的编程空间,编程开发者的网络收藏夹
学习永远不晚

C#使用CallContext缓存线程数据

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

C#使用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的(不跨线程传播)。

到此这篇关于C#使用CallContext缓存线程数据的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持编程网。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

C#使用CallContext缓存线程数据

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

C#怎么使用CallContext缓存线程数据

本篇内容主要讲解“C#怎么使用CallContext缓存线程数据”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C#怎么使用CallContext缓存线程数据”吧!一、CallContext 概述
2023-06-30

jQuery中数据缓存$.data怎么使用

这篇文章主要介绍“jQuery中数据缓存$.data怎么使用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“jQuery中数据缓存$.data怎么使用”文章能帮助大家解决问题。一、实现原理:对于DOM
2023-07-04

Django | 页面数据的缓存与使用

为什么要使用缓存?一个动态网站的基本权衡点就是,它是动态的。 每次用户请求页面,服务器会重新计算。从开销处理的角度来看,这比你读取一个现成的标准文件的代价要昂贵的多使用缓存,将多用户访问时基本相同的数据先缓存起来;这样当用户访问页面的时候,
2023-01-31

数据缓存如何在Spring Boot中使用

数据缓存如何在Spring Boot中使用?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。在实际开发中,对于要反复读写的数据,最好的处理方式是将之在内存中缓存一份,频繁的数据
2023-05-31

详解Spring Boot使用redis实现数据缓存

基于spring Boot 1.5.2.RELEASE版本,一方面验证与Redis的集成方法,另外了解使用方法。集成方法1、配置依赖修改pom.xml,增加如下内容。 org.sprin
2023-05-31

使用C语言怎么对线程对象和线程进行存储

使用C语言怎么对线程对象和线程进行存储?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。C语言是什么C语言是一门面向过程的、抽象化的通用程序设计语言,广泛应用于底层开发,使用C
2023-06-08

Spring Boot人使用Redis数据库的缓存机制

Spring Boot人使用Redis数据库的缓存机制?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。本文章牵涉到的技术点比较多:spring Data JPA、Redis、Sp
2023-05-31

如何使用SpringCache进行缓存数据库查询

这篇文章给大家分享的是有关如何使用SpringCache进行缓存数据库查询的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。SpringCache进行缓存数据库查询1、在SpringBoot的启动类上添加注解@Ena
2023-06-25

如何使用C#多线程处理多个队列数据

这篇文章主要介绍如何使用C#多线程处理多个队列数据,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!概述多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬
2023-06-15

使用C语言怎么存取数据

这篇文章将为大家详细讲解有关使用C语言怎么存取数据,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。数据的存储和取出整形的储存我们知道一个整形的存储是以补码的形式储存取出是原码的形式。比如:in
2023-06-06

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录