C# 两大线程本地存储解决方案:ThreadStatic 与 ThreadLocal

news/2025/1/10 20:31:22/文章来源:https://www.cnblogs.com/bcsg/p/18664673

C# 两大线程本地存储解决方案:ThreadStatic 与 ThreadLocal

一、线程本地存储

在 C# 中,static 关键字定义的变量,其作用域是在应用程序域(AppDomain)内共享的。因此,在多线程操作时,对同一个静态变量进行操作可能会导致并发问题,如锁竞争等。这种情况下,我们需要一种机制,使某些变量对每个线程独立,从而避免锁竞争问题。

线程本地存储(Thread-Local Storage,TLS)是一种机制,允许每个线程拥有自己的变量副本。这意味着每个线程对变量的访问都是独立的,互不干扰。

例如,线程安全的 ConcurrentBag 底层的实现就用到了 ThreadLocal 变量,来实现线程间的独立数据存储。

二、C# 中的解决方案

1. ThreadStatic 特性

概念与用法

ThreadStatic 是一个用于静态字段的特性,可以使字段在每个线程中都有一个独立的实例。

示例代码

internal class Program
{[ThreadStatic]private static int _threadSpecificData = -1;static void Main(string[] args){Task.Run(() =>{_threadSpecificData = 1;Console.WriteLine($"第1个线程,线程id: {Environment.CurrentManagedThreadId}: {_threadSpecificData}");});Task.Run(() =>{_threadSpecificData = 2;Console.WriteLine($"第2个线程,线程id:{Environment.CurrentManagedThreadId}: {_threadSpecificData}");});Console.ReadKey();}
}

输出:

第1个线程,线程id: 9: 1
第2个线程,线程id:8: 2

注意事项

只能用于 static 字段。
每个线程使用变量前,初始化变量。定义变量时,赋值的变量初始值,只会有一个线程的变量有这个初始值,其他线程变量副本是没有赋值的状态。特别主要引用类型的变量,会是null.

2. ThreadLocal 类

概念与用法

ThreadLocal<T> 是 .NET 提供的一个泛型类,用于更灵活地实现线程本地存储。它允许每个线程拥有独立的值,并支持通过工厂方法初始化每个线程的值。

示例代码

 internal class Program{static void Main(string[] args){ThreadLocal<int> _threadSpecificData = new ThreadLocal<int>(() => Thread.CurrentThread.ManagedThreadId);Task.Run(() =>{Console.WriteLine($"第1个线程,线程id: {Environment.CurrentManagedThreadId}: value: {_threadSpecificData}");});Task.Run(() =>{Console.WriteLine($"第1个线程,线程id: {Environment.CurrentManagedThreadId}: value: {_threadSpecificData}");});Console.ReadKey();}}

输出:

第1个线程,线程id: 8: value: 8
第1个线程,线程id: 6: value: 6

特点

支持 Func 委托延迟初始化

区别

特性 ThreadStatic ThreadLocal
使用范围 仅用于 static 字段 不限制
初始化方式 无法直接初始化,需显式赋值 支持延迟初始化
管理灵活性 简单,但功能受限 功能强大,适合复杂场景

三、线程本地存储到底存储在什么地方

线程本地存储的引用地址是存在 TLS(Thread Local Storage)上。
TLS(Thread Local Storage): 用于存储线程私有数据。
TEB(Thread Environment Block): 在线程的本地存储之上,是线程的环境块,保存了线程的上下文信息。
PEB(Process Environment Block): 进程环境块,用于存储进程级的全局信息。

以下是内存结构的示意图:

+----------------+
|   PEB          |
+----------------+
|   TEB          | <-- 多个线程
+----------------+
|   TLS          |
+----------------+

四、应用场景

示例 1:用于日志记录中的线程上下文

在日志记录中,通过线程本地存储可以为每个线程存储独立的上下文信息。在多线程应用程序中,每个线程可能需要记录不同级别的日志。使用ThreadLocal可以为每个线程分配一个独立的日志记录器实例,从而避免日志记录的竞争和同步问题。

internal class Program
{static void Main(string[] args){Task.Run(() =>{logger.SetContext("线程A");                logger.log("在执行第1步操作");Thread.Sleep(200);logger.log("在执行第2步操作");logger.log("执行完了");});Task.Run(() =>{logger.SetContext("线程B");logger.log("在执行第1步操作");logger.log("遇到异常,退出了");});Console.ReadKey();}
}class logger
{private static ThreadLocal<string> _context = new ThreadLocal<string>();public static void SetContext(string context){_context.Value = context;}public static void log(string message){Console.WriteLine($"[{_context.Value}]{message}");}
}

输出:

[线程A]在执行第1步操作
[线程B]在执行第1步操作
[线程B]遇到异常,退出了
[线程A]在执行第2步操作
[线程A]执行完了

五、总结

C# 提供了两种线程本地存储解决方案:
ThreadStatic:简单直接,但初始化灵活性较差。
ThreadLocal:功能强大,适合更复杂的场景。

根据具体应用场景选择合适的方案,可以显著提高程序的性能和线程安全性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/867306.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

iterm2

外观 这个github上的项目提供了很多主题 https://iterm2colorschemes.com/ 亮色主题感觉还可以的几个: OneHalfLight、BuiltinLight、BlulocoLight、Materil、ProLight、Tango Half Adapted 下面截图是基于 Tango Half Adapted用蓝色代替青色,黄色加深之后的效果(vim括号插件…

22. MDI窗口设计

一、什么是MDI窗口MDI 窗口(Multiple-Document Interface),又称多文档界面,它主要用于同时显示多个文档,每个文档显示在各自的窗口中。MDI 窗口中通常包含子菜单和窗口菜单,用于在窗口或文档之间进行切换。用 QMainWindow 建立的主界面,通常会同时建立或打开多个相互独立…

详解:订单履约系统规划

大家好,我是汤师爷~ 什么是订单履约系统? 订单履约是从消费者下单支付到收到商品的全流程管理过程,包括订单接收、订单派单、库存分配、仓储管理和物流配送等环节,核心目标是确保商品准时、准确地送达消费者手中。 通过订单履约系统,消费者可以实时了解商品的物流状态和预…

文件单独编译生成

编译设备树也最好在 source过的那个窗口中来编译错误提示如下: 同时在makefile文件修改时也要注意

In‐band Network Telemetry

#卫星 #遥测技术 #INT 一、INT是什么? INT,In‐band Network Telemetry ,带内网络遥监测。telemetry,英文原意是遥测技术。从其英文名称可以了解如下: a.In-band,说明监测指令及数据均在带内传输 b.telemetry ,说明是长距离,远程获取网络数据的方法。 想象一下卫星在…

潮汐指纹识别工具 : 在线网站识别利器

最近发现了一个免费在线收集网站信息的工具,感觉挺好用的,给各位推荐下。潮汐在线指纹识别是山东新潮信息技术有限公司安全团队提供的一个免费开源在线网站信息收集工具。 功能亮点: 全面扫描 只需输入目标网站的URL或IP地址,即可获取该网站的标题、中间件、操作系统、域名…

JAVA-Day 11:数组的静态初始化和遍历

数组的静态初始化和遍历 数组静态初始化格式 数组的静态初始化与遍历 完整格式:数据类型 [] 数组名=new 数据类型[]{元素1,元素2,元素3,....} 简化格式:数据类型 [] 数组名={元素1,元素2,元素3,....} []在数组名前后都可以 代码如下: int number[]={1,2,3,4,5};for (int …

JAVA-Day 10:Do...While循环语句

Do...While循环语句 Do...While循环格式 初始化语句; do{ 循环体语句; 条件控制语句; }while(条件判断语句); 例: 使用do...while循环输出1-10的和(包括10) int i=1;int count=0;do{count+=i;i++;}while(i<=10);System.out.println(count);代码运行结果如下图所示:

配置tigerVNC,登陆远程服务器

1.在远程服务器安装、配置 (1) sudo apt update sudo apt install xfce4 xfce4-goodies (2) 安装TigerVNC sudo apt install tigervnc-standalone-server (3) 配置vnc vncpasswd (4) 配置.vnc: vim ~/.vnc/xstartup 添加:cat .vnc/xstartup#!/bin/sh # 启动 D-Bus 会话 (如果未…

CF1993F2 Dyn-scripted Robot (Hard Version)

Dyn-scripted Robot (Hard Version) 题目链接。 Problem Easy Version:\(K \le n\)。 Hard Version:\(K \le 10^{12}\)。 一个 \(Oxy\) 平面上有一个 \(w \times h\) 矩形,矩形的左下方有点 \((0, 0)\) ,右上方有点 \((w, h)\) 。 您还有一个最初位于点 \((0, 0)\) 的机器人…

OmniNxt 论文阅读

来源: https://arxiv.org/html/2403.20085?_immersive_translate_auto_translate=1 标题: OmniNxt: A Fully Open-source and Compact Aerial Robot with Omnidirectional Visual Perception OmniNxt 感觉是, 取自 Omnidirectional 中 全的意思, Nxt 像是 Next 的意思,表…

2024-12-1-#{}与¥{}的区别-response

{}与¥{}的区别response实现重定向response响应字符数据response响应字节数据以及导入工具类实现响应