2020-07-06

.NET中的线程本地存储(TLS)与AsyncLocal(一)

一、TLS

      线程本地存储(Thread Local Storage),字面意思就是专属某个线程的存储空间。变量大体上分为全局变量和局部变量,一个进程中的所有线程共享地址空间,这个地址空间被划分为几个固有的区域,比如堆栈区,全局变量区等,全局变量存储在全局变量区,虚拟地址固定;局部变量存储在堆栈区,虚拟地址不固定。每个线程都有自己的栈空间,局部变量就存储在栈空间里面,虽然这个局部变量是与线程相联系的,但是这个局部变量不能在不同的函数栈中互相直接访问,但TLS可以,概括来讲,TLS是属于线程的"局部变量",作用域为线程作用域,而不像全局变量为全局作用域,局部变量为局部作用域,因为这个变量独属于这个线程,所以这个变量是线程安全的。

二、.NET中相关的类——ThreadLocal

      代码更直观,请看下面的代码:

 1 static void Main(string[] args) 2 { 3  ThreadLocal<int> threadLocal = new ThreadLocal<int>(); 4  //在主线程这个变量值为1 5  threadLocal.Value = 1; 6  new Thread(() => Console.WriteLine($"托管线程ID:{Thread.CurrentThread.ManagedThreadId} 值为:{threadLocal.Value++}")).Start(); 7  new Thread(() => Console.WriteLine($"托管线程ID:{Thread.CurrentThread.ManagedThreadId} 值为:{threadLocal.Value++}")).Start(); 8  new Thread(() => Console.WriteLine($"托管线程ID:{Thread.CurrentThread.ManagedThreadId} 值为:{threadLocal.Value++}")).Start(); 9  Console.WriteLine($"主线程ID:{Thread.CurrentThread.ManagedThreadId} 值为:{threadLocal.Value}");10 }



      输出结果如下:

image

      可以看见每个这个变量的值对于每个线程来说都是独立的,一个线程对这个变量的修改只会影响本线程的读取,每个线程都有一份拷贝。

      有什么用呢?或者使用场景是什么呢?我觉得就是一句话——当每个线程都需要一个唯一的变量的时候。

      比如早期版本的ASP.NET,每个线程处理一个Http请求,在处理这个Http请求的线程中,这个HttpContext在这个线程中是唯一的,所以在每个函数中都可以调用HttpContext.Current获得当前Http请求上下文对象,为了加深理解,请看下面的代码

 1 public class ConsoleContext 2  3 { 4  5  private static ThreadLocal<ConsoleContext> _tlsCCT = new ThreadLocal<ConsoleContext>(); 6  7  private string _consoleName; 8  9  public string ConsoleName { get => _consoleName; }10 11  public static ConsoleContext Current { get => _tlsCCT.Value; }12 13  public ConsoleContext(string consoleName)14 15   {16 17   _consoleName = consoleName;18 19   _tlsCCT.Value = this;20 21   }22 23  public static void ResetContext() => _tlsCCT.Value = null;24 25 }26 27 public static void Excute()28 29 {30 31  Thread.Sleep(1000 * new Random(DateTime.Now.Millisecond).Next(5,10));32 33  Console.WriteLine("进入PrintName()");34 35   PrintName();36 37 }38 39 public static void PrintName()40 41 {42 43  var name = ConsoleContext.Current.ConsoleName;44 45  Console.WriteLine($"当前托管线程ID:{Thread.CurrentThread.ManagedThreadId} name:{name}");46 47 }48 49 static void Main(string[] args)50 51 {52 53  while (true)54 55   {56 57   var name = Console.ReadLine();58 59   ThreadPool.QueueUserWorkItem(state =>60 61    {62 63    Console.WriteLine($"当前托管线程ID:{Thread.CurrentThread.ManagedThreadId} name:{name}");64 65    new ConsoleContext(name);66 67     Excute();68 69     ConsoleContext.ResetContext();70 71    });72 73   }74 75 }


      简单来说,我模拟了一个Web服务器的行为,监听请求(在这里是监听键盘输入),若没有请求过来,服务器程序阻塞,若有请求过来(在这里是键盘输入),服务器响应请求,生成当前请求上下文,并生成一个TLS变量,然后执行Excute函数(相当HttpContext流入处理管道),最后清空TLS变量中的值,因为该线程是线程池中的线程,会被复用用于处理其他请求,不清空TLS会生成脏数据。

.NET中的线程本地存储(TLS)与AsyncLocal(一)洋老板焦点科技tradeindia跨境微聊|口罩会是下一个亚马逊爆款么?3Dsellers亚马逊销量飙升的秘诀珠海游跟团多少钱2017珠海游价格珠海游价格

No comments:

Post a Comment