线程本地存储(Thread Local Storage),字面意思就是专属某个线程的存储空间。变量大致上分为全局变量和局部变量,一个进程中的全部线程共享地址空间,这个地址空间被划分为几个固有的区域,好比堆栈区,全局变量区等,全局变量存储在全局变量区,虚拟地址固定;局部变量存储在堆栈区,虚拟地址不固定。每一个线程都有本身的栈空间,局部变量就存储在栈空间里面,虽然这个局部变量是与线程相联系的,可是这个局部变量不能在不一样的函数栈中互相直接访问,但TLS能够,归纳来说,TLS是属于线程的“局部变量”,做用域为线程做用域,而不像全局变量为全局做用域,局部变量为局部做用域,由于这个变量独属于这个线程,因此这个变量是线程安全的。安全
代码更直观,请看下面的代码:服务器
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 }
输出结果以下:dom
能够看见每一个这个变量的值对于每一个线程来讲都是独立的,一个线程对这个变量的修改只会影响本线程的读取,每一个线程都有一份拷贝。函数
有什么用呢?或者使用场景是什么呢?我以为就是一句话——当每一个线程都须要一个惟一的变量的时候。this
好比早期版本的ASP.NET,每一个线程处理一个Http请求,在处理这个Http请求的线程中,这个HttpContext在这个线程中是惟一的,因此在每一个函数中均可以调用HttpContext.Current得到当前Http请求上下文对象,为了加深理解,请看下面的代码spa
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会生成脏数据。线程