背景

为了确保在线程中声明特定类型的变量,在每个线程中的值都是唯一的,不受到其他线程对该变量读写的影响。也就是俗称的线程本地存储 (TLS),可用于存储对线程和应用程序域唯一的数据。

例如:主线程中声明了变量A ,只能由主线程进行读取和写入。子线程虽然可以使用变量A(相当于复制一个A,可以对该变量进行读写),却无法读取和写入主线变量中A的值。

.net提供了3种方式:

1、线程相对静态字段 相对于线程的静态字段(ThreadStaticAttribute)

2、数据槽  LocalDataStoreSlot

3、ThreadLocal<T>

托管线程本地存储 (TLS) 可用于存储对线程和应用程序域唯一的数据。 .NET 提供了下面两种托管 TLS 使用方式:线程相对静态字段和数据槽。

数据槽 LocalDataStoreSlot简称DataSlot

背景:为了解决多线程竞用共享资源的问题,引入数据槽的概念,即将数据存放到线程的环境块中,使该数据只能单一线程访问.(属于线程空间上的开销)

Thread 静态类的内部有一个静态类LocalDataStore。该类维护这一个静态字典,该字典装着所有线程的LocalDataStoreSlot的变量。LocalDataStoreSlot类是ThreadLocal<object> Data封装。

  ①:AllocateNamedDataSlot命名槽位和AllocateDataSlot未命名槽位 解决线程竞用资源共享问题。

 

数据槽对于每个线程都是唯一的。 没有其他线程 (甚至子线程) 可以获取该数据。

.NET 提供了对于线程和应用程序域都是唯一的动态数据槽。 数据槽分为下列两种类型:命名槽和未命名槽。 两种类型都是使用 LocalDataStoreSlot 结构实现。LocalDataStoreSlot 封装的是internal ThreadLocal<object> Data 字段。

对于命名槽和未命名槽,请使用 Thread.SetDataThread.GetData 方法设置和检索槽中的信息。 这些静态方法始终处理当前正在执行它们的线程的数据。

命名槽非常便捷,因为可以在需要时检索槽,具体操作是将它的名称传递给 GetNamedDataSlot 方法,而不用维护对未命名槽的引用。 不过,如果另一个组件对线程相对存储使用相同的名称,并且线程同时执行你的组件和另一个组件的代码,这两个组件可能会相互损坏数据。 (此方案假定这两个组件都在同一个应用域中运行,并不旨在共享相同的数据。)

显然ThreadStatic特性只支持静态字段太受限制了,.NET线程类型中的LocalDataStoreSlot提供更好的TLS支持,但是性能不如上面介绍的ThreadStatic方法。注意:LocalDataStoreSlot有命名类型和非命名类型区分。

我们先来看看命名的LocalDataStoreSlot类型,可以通过Thread.AllocateNamedDataSlot来分配一个的空间,通过Thread.FreeNamedDataSlot来销毁一个的空间。

把线程相关的数据存储在LocalDataStoreSlot对象中,数据槽的获取和设置则通过Thread类型的GetData方法和SetData方法。

存放局部存储步骤:
1、申请数据槽

= Thread.GetNamedDataSlot("para");

如果不存在名为para的数据槽,将分配一个所有线程均可用的para数据槽
2、往数据槽存放数据

= new MyPara();
para.I = i;
Thread.SetData(slot,para);

3、如有必要,释放数据槽

Thread.FreeNamedDataSlot("para");

释放数据槽要小心,该操作将使所有线程存放在被释放的数据槽中的数据丢失。

读取局部存储步骤:
1、根据名字子线程局部存储中获取特定的数据槽

= Thread.GetNamedDataSlot("para");

2、从数据槽获取数据

= Thread.GetData(slot);
if (o != null)
{
 //转化为特定类型
 MyPara para = (MyPara) o ;
 //.
}
class Programe
{ // [ThreadStatic] public static string Username = "";
static void Main()
{
LocalDataStoreSlot Username = Thread.AllocateNamedDataSlot("userName");
Thread.SetData(Username, "mainthread");
Thread thread1 = new Thread(() => {
//Username = Thread.AllocateNamedDataSlot("userName"); 会报错,因为静态字典中已经存在了同名的数据槽

Username = Thread.AllocateNamedDataSlot("userName1");
Thread.SetData(Username, "Thread1"); Console.WriteLine($"Username1:{Thread.GetData(Username)}"); });
Thread thread2 = new Thread(() => { Username = Thread.AllocateNamedDataSlot("userName2"); Thread.SetData(Username, "Thread2"); Console.WriteLine($"Username2:{Thread.GetData(Username)}"); });
Thread thread3 = new Thread(() => { Username = Thread.AllocateNamedDataSlot("userName3"); Thread.SetData(Username, "Thread3"); Console.WriteLine($"Username3:{Thread.GetData(Username)}"); }); thread1.Start();
thread2.Start();
Console.WriteLine($"Username:{Thread.GetData(Username)}");
thread3.Start(); } } //输出结果:
//Username:mainthread
//Username:Thread1
//Username:Thread2
//Username:Thread3

【C# 线程】数据槽 LocalDataStoreSlot简称DataSlot的更多相关文章

  1. Entity Framework Context上下文管理(CallContext 数据槽)

    Context上下文管理 Q1:脏数据 Q2:一次逻辑操作中,会多次访问数据库,增加了数据库服务器的压力 >在一次逻辑操作中实现上下文实例唯一 方法一:单例模式:内存的爆炸式增长 在整个运行期间 ...

  2. C# 如何保证对象线程内唯一:数据槽(CallContext)

    如果说,一个对象保证全局唯一,大家肯定会想到一个经典的设计模式:单例模式,如果要使用的对象必须是线程内唯一的呢? 数据槽:CallContext,ok看下msdn对callcontent的解释. Ca ...

  3. C# 如何保证对象线程内唯一:数据槽(CallContext)【转载】

    如果说,一个对象保证全局唯一,大家肯定会想到一个经典的设计模式:单例模式,如果要使用的对象必须是线程内唯一的呢? 数据槽:CallContext,ok看下msdn对callcontent的解释. Ca ...

  4. 如何保证对象线程内唯一:数据槽(CallContext)

    CallContext 是类似于方法调用的线程本地存储区的专用集合对象,并提供对每个逻辑执行线程都唯一的数据槽.数据槽不在其他逻辑线程上的调用上下文之间共享.当 CallContext 沿执行代码路径 ...

  5. 第二节:深入剖析Thread的五大方法、数据槽、内存栅栏。

    一. Thread及其五大方法 Thread是.Net最早的多线程处理方式,它出现在.Net1.0时代,虽然现在已逐渐被微软所抛弃,微软强烈推荐使用Task(后面章节介绍),但从多线程完整性的角度上来 ...

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

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

  7. java 线程数据同步

    java 线程数据同步 由买票实例 //java线程实例 //线程数据同步 //卖票问题 //避免重复卖票 //线程 class xc1 implements Runnable{ //定义为静态,可以 ...

  8. WPF / Win Form:多线程去修改或访问UI线程数据的方法( winform 跨线程访问UI控件 )

    WPF:谈谈各种多线程去修改或访问UI线程数据的方法http://www.cnblogs.com/mgen/archive/2012/03/10/2389509.html 子线程非法访问UI线程的数据 ...

  9. java 使用volatile实现线程数据的共享

    java 使用volatile实现线程数据的共享 直接上代码看效果: public class VolatileTest extends Thread { private volatile boole ...

随机推荐

  1. golang中使用zap日志库

    1. 快速使用 package main import ( "go.uber.org/zap" "time" ) func main() { // 1. sug ...

  2. 科技爱好者周刊(第 176 期):中国法院承认 GPL 吗?

    这里记录每周值得分享的科技内容,周五发布. 本杂志开源(GitHub: ruanyf/weekly),欢迎提交 issue,投稿或推荐科技内容. 周刊讨论区的帖子<谁在招人?>,提供大量程 ...

  3. docker k8s安装

    docker安装 删除依赖包 sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docke ...

  4. springboot 配置mybatis 配置mapper.xml

    # 插件 进行配置 也可以用yml # 1. 配置 Tomcat 修改端口号 server.port=8848 server.context-path=/zxf #2.配置数据源 spring.dat ...

  5. Ubuntu 18.04 安装教程

    准备材料 Ubuntu安装U盘 足够的硬盘空间 未初始化的硬盘需要提前初始化 注意事项 Ubuntu安装盘的制作请参考我的另外一个博客,里面写清楚了怎么制作Ubuntu安装盘,步骤非常简单 请将要拿给 ...

  6. AT2401 [ARC072C] Alice in linear land

    基于观察,可以发现这样一条性质: 我们并不关心当前位置和终点的绝对关系,只在乎当前位置和终点的距离,当这个距离确定的时候接下来能走到的位置也是确定的. 基于这个观察可以发现,本质上每个位置的状态就是和 ...

  7. JavaScript多元运算符

    JavaScript多元运算符   JavaScript多元运算符 **实例:**` function test(9){ var a=4,b=11; return p > 1 ? p<b ...

  8. keystore文件

    [-] keystore操作 运行时签名文件路径debug 生成签名文件打包时使用 获取MD5和SH1 修改keystore文件密码 修改keystore文件别名 修改keystore文件别名的密码 ...

  9. k8s之Pod基础概念

    1. 资源限制 Pod是kubernetes中最小的资源管理组件,Pod也是最小化运行容器化应用的资源对象.一个Pod代表着集群中运行的一个进程.kubernetes中其他大多数组件都是围绕着Pod来 ...

  10. Scala函数高级篇

    一.匿名函数 没有名字的函数就是匿名函数,格式:(x:Int)=>{函数体} x:表示输入参数类型:Int:表示输入参数类型:函数体:表示具体代码逻辑 传递匿名函数至简原则: 参数的类型可以省略 ...