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

来源:https://www.cnblogs.com/hkfyf/archive/2020/07/06/13209283.html
-Advertisement-
Play Games

一、TLS 線程本地存儲(Thread Local Storage),字面意思就是專屬某個線程的存儲空間。變數大體上分為全局變數和局部變數,一個進程中的所有線程共用地址空間,這個地址空間被劃分為幾個固有的區域,比如堆棧區,全局變數區等,全局變數存儲在全局變數區,虛擬地址固定;局部變數存儲在堆棧區,虛... ...


一、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會生成臟數據。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 發年終獎這件事,在互聯網公司正在成為一種傳統,就像不加班都不好意思說是搞互聯網的一樣。 年終獎其實是一件非常有儀式感的事情:年末拿錢回家過年。 今天,和大家看一下那些互聯網行業被大家津津樂道且羡慕嫉妒的年終獎們,同時也期待一下今年的年終獎(嘿嘿嘿 看看阿裡、位元組跳動、華為等這些大廠的年終獎都發 ...
  • 我們一般創建的線程都是普通非守護線程,守護線程是為普通線程服務的。這個說法比較抽象。 具體一個很大的區別是: JVM中所有的線程都是守護線程的時候,JVM就可以退出了--JVM不會等待守護線程是否運行結束 如果還有一個或以上的非守護線程則不會退出 非守護線程例子 public static void ...
  • 一直想深入go語言,下定決心今年要狠抓go語言 | 文章名稱 | 文章鏈接 | | | | | Golang網路編程 | https://www.cnblogs.com/ZhuChangwu/p/13198872.html | | | | | | | ...
  • 一、實現Runnable介面 public class RunnableDemo implements Runnable { public void run() { try { Thread.sleep(100); } catch (InterruptedException e) { e.print ...
  • 從今天起,我將製作一個電影推薦項目,在此寫下博客,記錄每天的成果。 其實,從我發佈 C# 爬取貓眼電影數據 這篇博客後, 我就已經開始製作電影推薦項目了,今天寫下這篇博客,也是因為項目進度已經完成50%了,我就想在這一階段停一下,回顧之前學到的知識。 一、主要為手機端 考慮到項目要有實用性,我選擇了 ...
  • using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System. ...
  • 前言 上一篇【.Net Core微服務入門全紀錄(六)——EventBus-事件匯流排】中使用CAP完成了一個簡單的Eventbus,實現了服務之間的解耦和非同步調用,並且做到數據的最終一致性。這一篇將使用IdentityServer4來搭建一個鑒權中心,來完成授權認證相關的功能。 IdentitySe ...
  • private:私有成員,在類的內部才可以訪問。 protected:保護成員,該類內部和繼承類中可以訪問。 public:公共成員,完全公開,沒有訪問限制。 internal:當前程式集內可以訪問。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...