C#线程本地存储:ThreadStatic特性、LocalDataStoreSlot和ThreadLocal<T>

C#线程本地存储:LocalDataStoreSlot和ThreadLocal<T>

http://www.360doc.com/content/12/0709/22/5054188_223274400.shtml

 

1. 使用ThreadStatic特性

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

 

//TLS中的str变量
[ThreadStatic]
static string str = "hehe";

static void Main()
{
//另一个线程只会修改自己TLS中的hehe
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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值