.NET中的線程本地存儲(TLS)與AsyncLocal(一)

1、TLS

      線程本地存儲(Thread Local Storage),字面意思就是專屬某個線程的存儲空間。變量大致上分爲全局變量局部變量,一個進程中的全部線程共享地址空間,這個地址空間被劃分爲幾個固有的區域,好比堆棧區全局變量區等,全局變量存儲在全局變量區,虛擬地址固定;局部變量存儲在堆棧區,虛擬地址不固定。每一個線程都有本身的棧空間,局部變量就存儲在棧空間裏面,雖然這個局部變量是與線程相聯繫的,可是這個局部變量不能在不一樣的函數棧中互相直接訪問,但TLS能夠,歸納來說,TLS是屬於線程的「局部變量」,做用域爲線程做用域,而不像全局變量爲全局做用域,局部變量爲局部做用域,由於這個變量獨屬於這個線程,因此這個變量是線程安全的。安全

2、.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 }



      輸出結果以下:dom

image

      能夠看見每一個這個變量的值對於每一個線程來講都是獨立的,一個線程對這個變量的修改只會影響本線程的讀取,每一個線程都有一份拷貝。函數

      有什麼用呢?或者使用場景是什麼呢?我以爲就是一句話——當每一個線程都須要一個惟一的變量的時候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會生成髒數據。線程

相關文章
相關標籤/搜索