生活随笔
收集整理的这篇文章主要介绍了
多线程中Local Store Slot(本地存储槽)[转]
小编觉得挺不错的,现在分享给大家,帮大家做个参考.
1. 使用ThreadStatic特性
ThreadStatic特性是最简单的TLS使用,且只支持静态字段,只需要在字段上标记这个特性就可以了:
[ThreadStatic] static string str = "hehe"; static void Main() { //另一个线程只会修改自己TLS中的str变量 Thread th = new Thread(() => { str = "Mgen"; Display(); }); th.Start(); th.Join(); Display(); } static void Display() { Console.WriteLine("{0} {1}", Thread.CurrentThread.ManagedThreadId, str); } 运行结果:
1 hehe
3 Mgen
可以看到,str静态字段在两个线程中都是独立存储的,互相不会被修改。
2. 使用命名的LocalDataStoreSlot类型
显然ThreadStatic特性只支持静态字段太受限制了。.NET线程类型中的LocalDataStoreSlot提供更好的TLS支持。我们先来看看命名的LocalDataStoreSlot类型,可以通过Thread.AllocateNamedDataSlot来分配一个命名的空间,通过Thread.FreeNamedDataSlot来销毁一个命名的空间。空间数据的获取和设置则通过Thread类型的GetData方法和SetData方法。
static void Main() { //创建Slot LocalDataStoreSlot slot = Thread.AllocateNamedDataSlot("slot"); //设置TLS中的值 Thread.SetData(slot, "hehe"); //修改TLS的线程 Thread th = new Thread(() => { Thread.SetData(slot, "Mgen"); Display(); }); th.Start(); th.Join(); Display(); //清除Slot Thread.FreeNamedDataSlot("slot"); } //显示TLS中Slot值 static void Display() { LocalDataStoreSlot dataslot = Thread.GetNamedDataSlot("slot"); Console.WriteLine("{0} {1}", Thread.CurrentThread.ManagedThreadId, Thread.GetData(dataslot)); } 输出:
3 Mgen
1 hehe
3. 使用未命名的LocalDataStoreSlot类型
线程同样支持未命名的LocalDataStoreSlot,未命名的LocalDataStoreSlot不需要手动清除,分配则需要Thread.AllocateDataSlot方法。注意由于未命名的LocalDataStoreSlot没有名称,因此无法使用Thread.GetNamedDataSlot方法,只能在多个线程中引用同一个LocalDataStoreSlot才可以对TLS空间进行操作,将上面的命名的LocalDataStoreSlot代码改成未命名的LocalDataStoreSlot执行:
//静态LocalDataStoreSlot变量 static LocalDataStoreSlot slot; static void Main() { //创建Slot slot = Thread.AllocateDataSlot(); //设置TLS中的值 Thread.SetData(slot, "hehe"); //修改TLS的线程 Thread th = new Thread(() => { Thread.SetData(slot, "Mgen"); Display(); }); th.Start(); th.Join(); Display(); } //显示TLS中Slot值 static void Display() { Console.WriteLine("{0} {1}", Thread.CurrentThread.ManagedThreadId, Thread.GetData(slot)); } 4. 使用.NET 4.0的ThreadLocal<T>类型
.NET 4.0在线程方面加入了很多东西,其中就包括ThreadLocal<T>类型,他的出现更大的简化了TLS的操作。ThreadLocal<T>类型和Lazy<T>惊人相似,构造函数参数是Func<T>用来创建对象(当然也可以理解成对象的默认值),然后用Value属性来得到或者设置这个对象。ThreadLocal的操作或多或少有点像上面的未命名的LocalDataStoreSlot,但ThreadLocal感觉更简洁更好理解。
static ThreadLocal<string> local; static void Main() { //创建ThreadLocal并提供默认值 local = new ThreadLocal<string>(() => "hehe"); //修改TLS的线程 Thread th = new Thread(() => { local.Value = "Mgen"; Display(); }); th.Start(); th.Join(); Display(); } //显示TLS中数据值 static void Display() { Console.WriteLine("{0} {1}", Thread.CurrentThread.ManagedThreadId, local.Value); } 输出:
3 Mgen
1 hehe
5. 强调一下不同方法和TLS的默认值
上面代码都是一个一个线程设置值,另一个线程直接修改值然后输出,不会觉察到TLS中默认值的状况,下面专门强调一下不同方法的默认值状况。ThreadStatic不提供默认值:
[ThreadStatic] static int i = 123; static void Main() { //输出本地线程TLS数据值 Console.WriteLine(i); //输出另一个线程TLS数据值 ThreadPool.QueueUserWorkItem(_ => Console.WriteLine(i)); //控制台等待线程结束 Console.ReadKey(); } 输出:
123
0
显然本地线程TLS数据时123,而静态变量的默认值不会在另一个线程中初始化的。
LocalDataStoreSlot很容易可以看出来,不可能有默认值,因为初始化只能构造一个空间,而不能赋予它值,Thread.SetData显然只会在TLS中设置数据,还是用代码演示一下:
static LocalDataStoreSlot slot = Thread.AllocateDataSlot(); static void Main() { Thread.SetData(slot, 123); //输出本地线程TLS数据值 Console.WriteLine(Thread.GetData(slot)); //输出另一个线程TLS数据值 ThreadPool.QueueUserWorkItem(_ => Console.WriteLine(Thread.GetData(slot) == null)); //控制台等待线程结束 Console.ReadKey(); } 输出:
123
True
第二行是True,那么另一个线程中的数据是null。
最后重点:.NET 4.0后的ThreadLocal会提供默认值的,还记得我上面说的那句话“ThreadLocal的操作或多或少有点像上面的未命名的LocalDataStoreSlot”?有人可能会问那为什么要创造出ThreadLocal?还有一个很大的区别ThreadLocal可以提供TLS中数据的默认值。(另外还有ThreadLocal是泛型类,而LocalDataStoreSlot不是)。
static ThreadLocal<int> local = new ThreadLocal<int>(() => 123); static void Main() { //输出本地线程TLS数据值 Console.WriteLine(local.Value); //输出另一个线程TLS数据值 ThreadPool.QueueUserWorkItem(_ => Console.WriteLine(local.Value)); //控制台等待线程结束 Console.ReadKey(); } 输出:
123
123
这篇文章也可以参考
http://www.cnblogs.com/lulu/archive/2012/03/17/2403872.html
转载于:https://www.cnblogs.com/chenqingwei/p/5003688.html
总结
以上是生活随笔为你收集整理的多线程中Local Store Slot(本地存储槽)[转]的全部内容,希望文章能够帮你解决所遇到的问题。
如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。