线程局部存储(TLS)
存放局部存储步骤:
1、申请数据槽
LocalDataStoreSlot slot = Thread.GetNamedDataSlot("para");
如果不存在名为para的数据槽,将分配一个所有线程均可用的para数据槽
2、往数据槽存放数据
MyPara para = new MyPara();
para.I = i;
Thread.SetData(slot,para);
3、如有必要,释放数据槽
Thread.FreeNamedDataSlot("para");
释放数据槽要小心,该操作将使所有线程存放在被释放的数据槽中的数据丢失。
读取局部存储步骤:
1、根据名字子线程局部存储中获取特定的数据槽
LocalDataStoreSlot slot = Thread.GetNamedDataSlot("para");
2、从数据槽获取数据
Object o = Thread.GetData(slot);
if (o != null)
{
//转化为特定类型
MyPara para = (MyPara) o ;
//....
}
把下面代码生成mydll.dll
using System;
using System.Threading;
namespace Demo
{
public class MyPara : MarshalByRefObject
{
public int I;
}
public class MyDemoObj : MarshalByRefObject
{
public void demoTLS()
{
LocalDataStoreSlot slot = Thread.GetNamedDataSlot("para"); //根据名字获取数据槽
if(slot != null)
{
//获取数据
Object o = Thread.GetData(slot);
Console.WriteLine("得到对象\t{0}",o!=null);
if(o!=null)
{
//转化为特定的对象
MyPara para =(MyPara)o;
Console.WriteLine(" 应用域={0}\t线程号={1}\tpara= {2}",AppDomain.CurrentDomain.FriendlyName,Thread.CurrentThread.GetHashCode(),para.I);
}
else
{
Console.WriteLine("应用域={0}\t线程号={1}",AppDomain.CurrentDomain.FriendlyName,Thread.CurrentThread.GetHashCode());
}
}
}
public void setTLS(int i)
{
LocalDataStoreSlot slot = Thread.GetNamedDataSlot("para");
MyPara para = new MyPara();
para.I = i;
Thread.SetData(slot , para);
}
}
}
把下面代码生成demo.exe
using System;
using System.Threading;
using System.Runtime.Remoting; //ObjectHandle所在的命名空间
namespace AppThreadDemo
{
class App
{
static private void ShowInfo()
{
Console.WriteLine("当前应用域的名字为:{0}",AppDomain.CurrentDomain.FriendlyName);
Console.WriteLine("当前线程的代码为:{0}",Thread.CurrentThread.GetHashCode().ToString());
}
static private void demoThreadTLSAcrossThreadCallBack()
{
Console.WriteLine("\n");
App.ShowInfo();
Console.WriteLine("\n读取在另一个线程中创建的线程局部存储");
Demo.MyDemoObj obj = new Demo.MyDemoObj();
obj.demoTLS();
obj.setTLS(200);
obj.demoTLS();
Console.WriteLine("\n");
}
//主线程跟子线程在同一个应用域中执行,子线程不能获取主线程的局部存储中para
//数据槽的数据;子线程对该线程的局部存储中para数据槽中数据的修改,没有改变
//主线程局部存储区中para数据槽中的数据
static private void demoThreadTLS()
{
App.ShowInfo();
Demo.MyDemoObj obj = new Demo.MyDemoObj();
obj.setTLS(100);
Thread thread = new Thread(new ThreadStart(demoThreadTLSAcrossThreadCallBack));
thread.Start();
thread.Join();
obj.demoTLS();
}
//对于执行在不同应用域的线程数据槽的数据互不访问,即各自槽数据的修改不会影响对方的数据槽
//数据,他们也不能让在别的应用域上的线程访问
static private void demoThreadTLSAcrossAppDomain()
{
App.ShowInfo();
AppDomain child = AppDomain.CreateDomain("ChildDomain",null,null);
ObjectHandle oh = (ObjectHandle)child.CreateInstance("mydll","Demo.MyDemoObj");
Demo.MyDemoObj obj = (Demo.MyDemoObj)oh.Unwrap();
Console.WriteLine("\n在另外一个应用域中设置当前线程的局部存储,并获得");
obj.setTLS(100);
obj.demoTLS();
Console.WriteLine("\n在当前应用域中不能获得其他应用域中的当前线程的局部存储");
Demo.MyDemoObj obj2 = new Demo.MyDemoObj();
obj2.demoTLS();
Console.WriteLine("\n在当前应用域中设置当前线程的局部存储,并获得");
obj2.setTLS(200);
obj2.demoTLS();
Console.WriteLine("\n当前应用域中设置当前的线程的局部存储不会改变当前线程在另一个应用域中的局部存储");
obj.demoTLS();
}
//对于线程池中的工作线程,也可以有自己的线程局部存储.但因为工作线程是按需分配的,可能存在一个工作线程
//执行多个逻辑线程(逻辑线程即工作队列中的一项任务),即一个物理线程的局部存储可以对应了几个逻辑线程的局部存储.
//下面代码显示了:逻辑线程没有独立的线程局部存储,它的局部存储位于执行逻辑线程的物理线程的局部存储中,
//这样第2个逻辑线程执行时从物理线程的局部存储中获得para参数.要是逻辑线程位于不同的应用域则获取数据
//槽中的数据会失败,代码见demoLogicalThreadTLSAcrossDoMain方法.
static private void demoLogicalThreadTLSCallBack(Object state)
{
Console.WriteLine("\n");
App.ShowInfo();
Demo.MyDemoObj obj = new Demo.MyDemoObj();
obj.demoTLS();
obj.setTLS(100);
obj.demoTLS();
((AutoResetEvent)state).Set();
}
static private void demoLogicalTreadTLS()
{
App.ShowInfo();
Console.WriteLine("\n执行第1个工作池逻辑线程");
AutoResetEvent asyncOpIsDone = new AutoResetEvent(false);
ThreadPool.QueueUserWorkItem(new WaitCallback(App.demoLogicalThreadTLSCallBack),asyncOpIsDone);
asyncOpIsDone.WaitOne();
Console.WriteLine("\n执行第2个工作池逻辑线程");
ThreadPool.QueueUserWorkItem(new WaitCallback(App.demoLogicalThreadTLSCallBack),asyncOpIsDone);
asyncOpIsDone.WaitOne();
Console.WriteLine("\n执行第3个工作池逻辑线程");
ThreadPool.QueueUserWorkItem(new WaitCallback(App.demoLogicalThreadTLSCallBack),asyncOpIsDone);
asyncOpIsDone.WaitOne();
}
static private void demoLogicalThreadTLSCallBackAcrossDomain(Object state)
{
Console.WriteLine("\n");
AppDomain child = AppDomain.CreateDomain("ChildDomain",null,null);
ObjectHandle oh = (ObjectHandle) child.CreateInstance("mydll","Demo.MyDemoObj");
Demo.MyDemoObj obj = (Demo.MyDemoObj)oh.Unwrap();
obj.demoTLS();
obj.setTLS(500);
obj.demoTLS();
((AutoResetEvent)state).Set();
}
static private void demoLogicalThreadTLSAcrossDoMain()
{
App.ShowInfo();
Console.WriteLine("\n执行第1个工作池逻辑线程");
AutoResetEvent asyncOpIsDone = new AutoResetEvent(false);
ThreadPool.QueueUserWorkItem(new WaitCallback(App.demoLogicalThreadTLSCallBack),asyncOpIsDone);
asyncOpIsDone.WaitOne();
Console.WriteLine("\n执行第2个工作池逻辑线程");
ThreadPool.QueueUserWorkItem(new WaitCallback(App.demoLogicalThreadTLSCallBackAcrossDomain),asyncOpIsDone);
asyncOpIsDone.WaitOne();
Console.WriteLine("执行结束");
}
static int Main(string[] args)
{
//demoThreadTLS();
//demoThreadTLSAcrossAppDomain();
//demoLogicalTreadTLS();
demoLogicalThreadTLSAcrossDoMain();
return 0;
}
}
}
局部线程的总结:
1.主线程跟子线程在同一个应用域中执行,子线程不能获取主线程的局部存储中para数据槽的数据;子线程对该线程的局部存储中para数据槽中数据的修改,没有改变主线程局部存储区中para数据槽中的数据
2.对于执行在不同应用域的线程数据槽的数据互不访问,即各自槽数据的修改不会影响对方的数据槽数据,他们也不能让在别的应用域上的线程访问
3.对于线程池中的工作线程,也可以有自己的线程局部存储.但因为工作线程是按需分配的,可能存在一个工作线程执行多个逻辑线程(逻辑线程即工作队列中的一项任务),即一个物理线程的局部存储可以对应了几个逻辑线程的局部存储.
要是逻辑线程位于不同的应用域则获取数据槽中的数据会失败.