網站首頁 編程語言 正文
在.Net 4.0的Thread里,新增了線程局部變量(ThreadLocal)類,可以很方便的實現線程專有存儲。
應用場景
線程專有存儲應被用于這樣的多線程應用:它們經常訪問那些邏輯上是全局的、而物理上是專有于每個線程的對象。首先我們看如下這樣一個例子
string errorMessage;
void Process()
{
bool ret = Run();
if (!ret && needDebug)
{
Console.WriteLine(errorMessage);
}
}
bool Run()
{
try
{
//…-- do something
return true;
}
catch (Exception e)
{
errorMessage = e.Message;
return false;
}
}
這個函數中,Process為主體函數,當它調用Run函數失敗后,為調式方便,打出Run函數的錯誤信息。錯誤信息采用成員變量errorMessage存放,為了減少Run函數的參數。
這種通過成員變量errorMessage在函數間傳遞信息的方式在單線程程序中可以很好的工作,但是在多線程應用時卻往往會發生一些微妙的問題:當兩個線程同時執行Run函數時,先執行的會被后執行的線程覆蓋,導致輸出了錯誤的后執行的線程的調試信息。發生類似數據庫的臟讀錯誤。
解決方案:
最直接的解決方案有兩種:
加鎖:在Process中加鎖,保證沒有兩個線程同時訪問errorMessage
修改Run函數為bool?Run(out string errorMessage)的形式,不通過errorMessage共享數據,使其支持并發操作。
這兩種方式都是有效的,但都有一些不足:加鎖時獲取和釋放互斥體有一個不小的開銷,當共享的數據較多時修改Run函數會導致Run函數變得很難看,并且可能會由于改動較大而導致大規模重構。
針對上述兩種方式的不足,人們提出了線程專有存儲的解決方案,使用ThreadLocal類的解決方案如下:
ThreadLocal<string> errorMessage = new ThreadLocal<string> ();
void Process()
{
bool ret = Run();
if (!ret && needDebug)
{
Console.WriteLine(errorMessage);
}
}
bool Run()
{
try
{
…- do something
return true;
}
catch (Exception e)
{
errorMessage.Value=e.Message;
return false;
}
}
ThreadLocal類在每個線程下都分配一個獨立實例副本,每個線程都只訪問到自己的實例,不會影響其它線程,從而解決讀臟數據的問題。
ThreadLocal類也不是什么新概念,在C++、Java等語言的線程庫中都有相關實現,一些語言編譯器實現(如IBM XL FORTRAN)中甚至在語言的層次提供了直接的支持。其實實現的思路很簡單:在ThreadLocal類中有一個哈希表,根據線程ID為key用于存儲每一個線程的變量的副本。由于現在沒啥相關資料,并且也是beta版的,我也懶得對.Net中的具體實現和性能進一步分析。
和上面的兩種方式相比,線程專有存儲有如下好處:
- 效率:線程專有存儲可實現成無需對線程專有數據進行鎖定。例如,通過將errno放入線程專有存儲中,每個線程都可以可靠地設置和測試該線程中的方法的完成狀態,而無需使用復雜的同步協議。這排除了線程中共享數據的鎖定開銷,比起獲取和釋放互斥體要更為迅捷。
- 易于使用:對于應用程序員來說,線程專有存儲使用起來很簡單,因為系統開發者可以通過數據抽象或宏來使線程專有存儲的使用在源碼級完全透明化。
但也存在如下缺點:
- 它鼓勵了(線程安全的)全局變量的使用:許多應用不要求多個線程通過公用訪問點來訪問線程專有的數據。如果是這樣,數據的存儲應使只有擁有該數據的線程可對它進行訪問。
- 它隱藏了系統的結構:線程專有存儲的使用隱藏了應用中的對象之間的關系,可能會導致應用更難被理解。
適用性
應用有以下特性時可使用線程專有存儲:
- 應用最初的編寫假定了單線程控制,并正在被移植到多線程環境,而又不能改變現有API
- 應用含有多個占先式線程控制,可以任意的調度順序并發執行;
- 每個線程控制調用一系列方法,這些方法共享只對該線程來說是公用的數據;
- 在每個線程中被對象共享的數據必須通過一個全局可見的訪問點來訪問;
- 訪問點"邏輯地"與其他線程共享,但在"物理上" 對于每個線程卻是唯一的;
- 數據在方法間隱式地傳遞,而不是經由參數顯式地傳遞。
理解上面描述的特性對于使用(或不使用)線程專有存儲模式來說是至關緊要的。例如,UNIX errno變量是一個數據例子:(1)邏輯上全局,但是物理上線程專有,以及(2)在方法間隱式地傳遞。
當應用有以下特性時,不要使用線程專有存儲模式:
- 多個線程為單個任務協同工作,該任務需要并發訪問共享數據。
例如,多線程應用可以對在內存中的數據庫并發地進行讀寫。在這樣的情況下,線程必須共享不是線程專有的記錄和表。如果使用線程專有存儲來存儲此數據庫,線程就不能共享這些數據。因而,對數據庫記錄的訪問必須通過同步原語(例如,互斥體)來控制,以使線程能在共享數據上協作。 - 維護物理和邏輯上都分離的數據要更為直觀和高效。
例如,通過將數據作為參數顯式地傳遞給所有方法,有可能使線程訪問僅在每個線程中可見的數據。在這樣的情況下,線程專有存儲模式有可能是不必要的。
原文鏈接:https://www.cnblogs.com/TianFang/archive/2009/11/15/1603476.html
相關推薦
- 2023-02-26 GoLang中panic與recover函數以及defer語句超詳細講解_Golang
- 2022-10-04 C語言指針和數組深入探究使用方法_C 語言
- 2024-03-04 layui樹形組件獲取復選框選中的id,禁用選中父節點后自動選中子節點功能
- 2022-09-17 python?df遍歷的N種方式(小結)_python
- 2023-02-17 Go語言如何實現TCP通信詳解_Golang
- 2022-04-01 本地存儲(Local Storage) 和 會話存儲(Session Storage)
- 2022-12-11 React?狀態的不變性實例詳解_React
- 2022-02-22 el-step中多個步驟中的表單一起提交
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支