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

多线程中Local Store Slot(本地存储槽)[转]的更多相关文章

  1. 多线程中Local Store Slot(本地存储槽)

    在Java中有一种ThreadLocal机制,为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突.从线程的角度看,就好像每一个线程都完全 ...

  2. 与众不同 windows phone (7) - Local Database(本地数据库)

    原文:与众不同 windows phone (7) - Local Database(本地数据库) [索引页][源码下载] 与众不同 windows phone (7) - Local Databas ...

  3. Java Volatile关键字 以及long,double在多线程中的应用

    概念: volatile关键字,官方解释:volatile可以保证可见性.顺序性.一致性. 可见性:volatile修饰的对象在加载时会告知JVM,对象在CPU的缓存上对多个线程是同时可见的. 顺序性 ...

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

    本文转载自:http://www.cnblogs.com/promise-7/articles/2354077.html 一.Lock定义     lock 关键字可以用来确保代码块完成运行,而不会被 ...

  5. ubuntu中使用nginx把本地80端口转到其他端口

    ubuntu中使用nginx把本地80端口转到其他端口 因为只是在开发的过程中遇到要使用域名的方式访问, 而linux默认把1024以下的端口全部禁用. 在网上找了N多方式开启80端口无果后, 方才想 ...

  6. Java多线程中易混淆的概念

    概述 最近在看<ThinKing In Java>,看到多线程章节时觉得有一些概念比较容易混淆有必要总结一下,虽然都不是新的东西,不过还是蛮重要,很基本的,在开发或阅读源码中经常会遇到,在 ...

  7. 多线程中lock用法的经典实例

    多线程中lock用法的经典实例 一.Lock定义     lock 关键字可以用来确保代码块完成运行,而不会被其他线程中断.它可以把一段代码定义为互斥段(critical section),互斥段在一 ...

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

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

  9. java多线程中的三种特性

    java多线程中的三种特性 原子性(Atomicity) 原子性是指在一个操作中就是cpu不可以在中途暂停然后再调度,既不被中断操作,要不执行完成,要不就不执行. 如果一个操作时原子性的,那么多线程并 ...

随机推荐

  1. Linux命令应用大词典-第40章 网络客户端

    40.1 elinks:字符模式的Web浏览器 40.2 wget:从Web网站下载文件 40.3 curl:传输URL 40.4 lynx:通用分布式信息的万维网浏览器 40.5 lftp:实现文件 ...

  2. Java学习笔记-13.创建窗口和程序片

    1.init()方法:程序片第一次被创建,初次运行初始化程序片时调用. start()方法:每当程序片进入web浏览器中,并且允许程序片启动他的常规操作时调用(特殊的程序片被stop()关闭):同样在 ...

  3. 【shell 练习2】产生随机数的方法总结

    一.产生随机数 ()RANDOM 产生随机数 [root@localhost ~]# echo $RANDOM [root@localhost ~]# )) #想要生成八个随机数,随便加一个八位的数字 ...

  4. Python+Flask+Gunicorn 项目实战(一) 从零开始,写一个Markdown解析器 —— 初体验

    (一)前言 在开始学习之前,你需要确保你对Python, JavaScript, HTML, Markdown语法有非常基础的了解.项目的源码你可以在 https://github.com/zhu-y ...

  5. hadoop参数(未完).md

    我X,有违禁词.麻烦提醒一下哪个词好吗?

  6. 欢迎来怼--第七次Scrum会议

    一.小组信息 队名:欢迎来怼 小组成员: 队长:田继平 成员:李圆圆,葛美义,王伟东,姜珊,邵朔,冉华 小组照片 二.开会信息 时间:2017/10/19 17:05~17:17,总计12min. 地 ...

  7. PHPCMS v9表单向导中怎么加入验证码

    表单想到比较简单,所以没有加入验证码的功能.网上的类似教程又大多数不准确.所以亲自测试了一下,发现下面的方法是可用的.希望对有需求的朋友们有所帮助. 1.首先是调用表单的页面加入验证码.表单js调用模 ...

  8. c# 计算两个时间的时间差

    //计算2个日期之间的天数差 DateTime dt1 = Convert.ToDateTime("2007-8-1"); DateTime dt2 = Convert.ToDat ...

  9. oracle数据库之PL/SQL 流程控制语句

    介绍 PL/SQL 的流程控制语句, 包括如下三类: 1.控制语句: IF 语句 2.循环语句: LOOP 语句, EXIT 语句 3.顺序语句: GOTO 语句, NULL 语句 一 条件语句 IF ...

  10. A4

    队名:起床一起肝活队 组长博客:博客链接 作业博客:班级博客本次作业的链接 组员情况 组员1(队长):白晨曦 过去两天完成了哪些任务 描述: 很胖,刚学,照猫画虎做了登录与注册界面. 展示GitHub ...