1. 使用ThreadStatic特性

ThreadStatic特性是最简单的TLS使用,且只支持静态字段,只需要在字段上标记这个特性就可以了:

  1. [ThreadStatic]
  2. static string str = "hehe";
  3. static void Main()
  4. {
  5. //另一个线程只会修改自己TLS中的str变量
  6. Thread th = new Thread(() => { str = "Mgen"; Display(); });
  7. th.Start();
  8. th.Join();
  9. Display();
  10. }
  11. static void Display()
  12. {
  13. Console.WriteLine("{0} {1}", Thread.CurrentThread.ManagedThreadId, str);
  14. }

运行结果:

1 hehe
3 Mgen

可以看到,str静态字段在两个线程中都是独立存储的,互相不会被修改。

2. 使用命名的LocalDataStoreSlot类型

显然ThreadStatic特性只支持静态字段太受限制了。.NET线程类型中的LocalDataStoreSlot提供更好的TLS支持。我们先来看看命名的LocalDataStoreSlot类型,可以通过Thread.AllocateNamedDataSlot来分配一个命名的空间,通过Thread.FreeNamedDataSlot来销毁一个命名的空间。空间数据的获取和设置则通过Thread类型的GetData方法和SetData方法。

  1. static void Main()
  2. {
  3. //创建Slot
  4. LocalDataStoreSlot slot = Thread.AllocateNamedDataSlot("slot");
  5. //设置TLS中的值
  6. Thread.SetData(slot, "hehe");
  7. //修改TLS的线程
  8. Thread th = new Thread(() =>
  9. {
  10. Thread.SetData(slot, "Mgen");
  11. Display();
  12. });
  13. th.Start();
  14. th.Join();
  15. Display();
  16. //清除Slot
  17. Thread.FreeNamedDataSlot("slot");
  18. }
  19. //显示TLS中Slot值
  20. static void Display()
  21. {
  22. LocalDataStoreSlot dataslot = Thread.GetNamedDataSlot("slot");
  23. Console.WriteLine("{0} {1}", Thread.CurrentThread.ManagedThreadId, Thread.GetData(dataslot));
  24. }

输出:

3 Mgen
1 hehe

3. 使用未命名的LocalDataStoreSlot类型

线程同样支持未命名的LocalDataStoreSlot,未命名的LocalDataStoreSlot不需要手动清除,分配则需要Thread.AllocateDataSlot方法。注意由于未命名的LocalDataStoreSlot没有名称,因此无法使用Thread.GetNamedDataSlot方法,只能在多个线程中引用同一个LocalDataStoreSlot才可以对TLS空间进行操作,将上面的命名的LocalDataStoreSlot代码改成未命名的LocalDataStoreSlot执行:

  1. //静态LocalDataStoreSlot变量
  2. static LocalDataStoreSlot slot;
  3. static void Main()
  4. {
  5. //创建Slot
  6. slot = Thread.AllocateDataSlot();
  7. //设置TLS中的值
  8. Thread.SetData(slot, "hehe");
  9. //修改TLS的线程
  10. Thread th = new Thread(() =>
  11. {
  12. Thread.SetData(slot, "Mgen");
  13. Display();
  14. });
  15. th.Start();
  16. th.Join();
  17. Display();
  18. }
  19. //显示TLS中Slot值
  20. static void Display()
  21. {
  22. Console.WriteLine("{0} {1}", Thread.CurrentThread.ManagedThreadId, Thread.GetData(slot));
  23. }

4. 使用.NET 4.0的ThreadLocal<T>类型

.NET 4.0在线程方面加入了很多东西,其中就包括ThreadLocal<T>类型,他的出现更大的简化了TLS的操作。ThreadLocal<T>类型和Lazy<T>惊人相似,构造函数参数是Func<T>用来创建对象(当然也可以理解成对象的默认值),然后用Value属性来得到或者设置这个对象。ThreadLocal的操作或多或少有点像上面的未命名的LocalDataStoreSlot,但ThreadLocal感觉更简洁更好理解。

  1. static ThreadLocal<string> local;
  2. static void Main()
  3. {
  4. //创建ThreadLocal并提供默认值
  5. local = new ThreadLocal<string>(() => "hehe");
  6. //修改TLS的线程
  7. Thread th = new Thread(() =>
  8. {
  9. local.Value = "Mgen";
  10. Display();
  11. });
  12. th.Start();
  13. th.Join();
  14. Display();
  15. }
  16. //显示TLS中数据值
  17. static void Display()
  18. {
  19. Console.WriteLine("{0} {1}", Thread.CurrentThread.ManagedThreadId, local.Value);
  20. }

输出:

3 Mgen

1 hehe

5. 强调一下不同方法和TLS的默认值
上面代码都是一个一个线程设置值,另一个线程直接修改值然后输出,不会觉察到TLS中默认值的状况,下面专门强调一下不同方法的默认值状况。ThreadStatic不提供默认值:

  1. [ThreadStatic]
  2. static int i = 123;
  3. static void Main()
  4. {
  5. //输出本地线程TLS数据值
  6. Console.WriteLine(i);
  7. //输出另一个线程TLS数据值
  8. ThreadPool.QueueUserWorkItem(_ => Console.WriteLine(i));
  9. //控制台等待线程结束
  10. Console.ReadKey();
  11. }

输出:
123

0

显然本地线程TLS数据时123,而静态变量的默认值不会在另一个线程中初始化的。

LocalDataStoreSlot很容易可以看出来,不可能有默认值,因为初始化只能构造一个空间,而不能赋予它值,Thread.SetData显然只会在TLS中设置数据,还是用代码演示一下:

  1. static LocalDataStoreSlot slot = Thread.AllocateDataSlot();
  2. static void Main()
  3. {
  4. Thread.SetData(slot, 123);
  5. //输出本地线程TLS数据值
  6. Console.WriteLine(Thread.GetData(slot));
  7. //输出另一个线程TLS数据值
  8. ThreadPool.QueueUserWorkItem(_ => Console.WriteLine(Thread.GetData(slot) == null));
  9. //控制台等待线程结束
  10. Console.ReadKey();
  11. }

输出:
123
True
第二行是True,那么另一个线程中的数据是null。

最后重点:.NET 4.0后的ThreadLocal会提供默认值的,还记得我上面说的那句话“ThreadLocal的操作或多或少有点像上面的未命名的LocalDataStoreSlot”?有人可能会问那为什么要创造出ThreadLocal?还有一个很大的区别ThreadLocal可以提供TLS中数据的默认值。(另外还有ThreadLocal是泛型类,而LocalDataStoreSlot不是)。

  1. static ThreadLocal<int> local = new ThreadLocal<int>(() => 123);
  2. static void Main()
  3. {
  4. //输出本地线程TLS数据值
  5. Console.WriteLine(local.Value);
  6. //输出另一个线程TLS数据值
  7. ThreadPool.QueueUserWorkItem(_ => Console.WriteLine(local.Value));
  8. //控制台等待线程结束
  9. Console.ReadKey();
  10. }

输出:
123
123

这篇文章也可以参考

http://www.cnblogs.com/lulu/archive/2012/03/17/2403872.html

转载于:https://www.cnblogs.com/chenqingwei/p/5003688.html

多线程中Local Store Slot(本地存储槽)[转]相关推荐

  1. windows的服务中的登录身份本地系统账户、本地服务账户和网络服务账户修改

    以一个redis服务为例: 一个redis注册服务后一般是网络服务账户,但是当系统不存在网络服务账户时,就会导致redis服务无法正常启动.接下来修改redis服务的登录身份. cmd下输入如下命令: ...

  2. 多线程中的应用之队列(queue)

    队列queue 多应用在多线程中,对于多线程访问共享变量时,队列queue是线程安全的. 从queue队列的实现来看,队列使用了1个线程互斥锁(pthread.Lock()),以及3个条件标量(pth ...

  3. Qt多线程中的信号与槽

    文章目录 1 多线程中的信号与槽 2 对象的依附性 2.1 对象的依附性 2.2 开启线程事件循环 2.3 线程事件循环的结束 2.4 设计实例 3 信号与槽的连接方式 3.1 Qt::DirectC ...

  4. c# 多线程中lock用法的经典实例

    一.Lock定义     lock 关键字可以用来确保代码块完成运行,而不会被其他线程中断.它可以把一段代码定义为互斥段(critical section),互斥段在一个时刻内只允许一个线程进入执行, ...

  5. 2008 mysql 本地安全_如何在Linux系统中建立mysql的本地安全机制?

    如何在Linux系统中对MySQL进行本地的安全机制?今天我们将给大家介绍下具体的操作方法. local-infile=0#关闭远程连接,即3306端口.这是LinuxMySQL的默认监听端口.由于此 ...

  6. vue-cli3.x中使用axios发送请求,配合webpack中的devServer编写本地mock数据接口(get/post/put/delete)...

    vue-cli3.x中使用axios发送请求,配合webpack中的devServer编写本地mock数据接口(get/post/put/delete) 手把手式笔记 Axios配置 安装 axios ...

  7. 关于多线程中的面试题

    关于多线程中的面试题 常见面试的 1.现在有T1.T2.T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行? 答:使用join就OK了. public class ThreeThr ...

  8. docker如何将镜像中的文件下载到本地

    1. 启动本地镜像. 2.使用命令下载镜像中的文件到本地磁盘. docker cp 3b4550732f7d:/usr/local/pipenet-docker-patrol/ /mnt/c/User ...

  9. Qt 多线程中地信号与槽

    Qt 多线程中地信号与槽 函数原型: 1 QObject::connect(const QObject *sender, const char *signal, const QObject *rece ...

最新文章

  1. 信息系统监理师题库_信息系统监理题库
  2. 连不上机器判断机器状态_KUKA机器人为什么KPP故障联不上,断电重启hou报RDC连接连接不上...
  3. 利用SecureCRT在linux与Windows之间传输文件
  4. 用稳压管保护单片机引脚_一步一步,全程揭开单片机的原理,让做电子变得轻松自如!...
  5. java 动态转换器_非常简单的Java动态转换
  6. ES6变量的解构赋值注意点及用途(补充)
  7. 漂浮广告代码 php,JS带关闭按钮的网页漂浮广告代码
  8. Python3 图片添加水印
  9. 守望先锋app(2)
  10. c#计算圆或者圆柱面积体积代码
  11. 《Spring Cloud Netflix》--服务注册和服务发现-Eureka的深入了解
  12. 【CH01】创建、初始化SpringBoot项目与项目git管理
  13. 网易16年春季实习生招聘的一道算法题
  14. ROS1云课→18一键配置
  15. WPF+WCF一步一步打造音频聊天室(二):文字聊天和白板共享
  16. OpenGL ES EGL eglCreatePbufferSurface
  17. CList 动态链表的学习笔记
  18. 查表法求sin和cos
  19. Android开发-基本概念小整理(二)为了面试的小伙伴们所准备~~
  20. wwdc2019_wwdc 20愿望清单

热门文章

  1. 环形缓冲区ringbuffer
  2. HTML技巧100例(三)
  3. csmar eco research
  4. 无风险对冲组合的设计
  5. 我对于大学选择的理解
  6. 把十进制的n转化成r进制的数的模板
  7. PA银行面试之炮灰之程
  8. linux | 网卡驱动
  9. 洛谷 1351 联合权值——树形dp
  10. 2017第八届中国跨境电商峰会暨展览将在11月底召开!