網站首頁 編程語言 正文
一、CallContext 概述
命名空間:System.Runtime.Remoting.Messaging
CallContext 用于提供與執行代碼路徑一起傳送的屬性集,直白講就是:提供線程(多線程/單線程)代碼執行路徑中數據傳遞的能力。
當對另一個 AppDomain 中的對象進行遠程方法調用時,CallContext 類將生成一個與該遠程調用一起傳播的 LogicalCallContext 實例。只有公開 ILogicalThreadAffinative 接口并存儲在 CallContext 中的對象被在 LogicalCallContext 中傳播到 AppDomain 外部。
CallContext成員
- SetData:??? 存儲給定的對象并將其與指定名稱關聯。
- GetData:??? 從CallContext中檢索具有指定名稱的對象????
- LogicalSetData:??? 將給定的對象存儲在邏輯調用上下文,并將其與指定名稱關聯。可用于多線程環境
- LogicalGetData:???? 從邏輯調用上下文中檢索具有指定名稱的對象。可用于多線程環境
- FreeNamedDataSlot:??? 清空具有指定名稱的數據槽。可用于多線程環境
- HostContext屬性:???? 獲取或設置與當前線程相關聯的主機上下文。在Web環境下等于System.Web.HttpContext.Current
GetData、SetData
只能用于單線程環境,如果發生了線程切換,存儲的數據也會隨之丟失
可以用于同一線程中的不同地方,傳遞數據
LogicalSetData、LogicalGetData
- LogicalSetData、LogicalGetData可用于在多線程環境下傳遞數據;
- LogicalSetData只是存儲當前線程以及子線程的數據槽
- LogicalGetData獲取的是當前線程或父線程的數據槽對象,拿到的是對象的引用
- FreeNamedDataSlot清除當前線程,之前已經運行子任務,不受影響,不能清除子線程的數據槽;
二、?CallContext不跨線程傳播的方法:GetData、SetData
可以利用CallContext?實現單例,默認情況下,CallContext?的數據不跨線程傳播。
1、在處理多組件共用Context時非常有用,比如常見的EF 可以將實例的DBEntity存儲在其中,可以一次訪問只實例化一次,便于管理且不用多次實例訪問對象
public static class DbContextHelper
{
private static DbContext context = null;
private const string SessionKey_DbContext = "Entities";
public static DbContext GetDbContext()
{
if (CallContext.GetData(SessionKey_DbContext) == null)
{
CallContext.SetData(SessionKey_DbContext, new Entities());
}
return CallContext.GetData(SessionKey_DbContext) as Entities;
}
}
2、類單例
void Main()
{
MyAppContext.Current.FirstName = "a";
Console.Write(MyAppContext.Current.FirstName);
}
public class MyAppContext
{
const string contextKey = "MyAppContext:ContextKey";
public string FirstName { get; set; }
public static MyAppContext Current
{
get
{
if (CallContext.GetData(contextKey) == null)
{
CallContext.SetData(contextKey, new MyAppContext());
}
return CallContext.GetData(SessionKey_DbContext) as MyAppContext;
}
}
}
三、 CallContext跨線程傳播的方法:ILogicalSetData、LogicalGetData
要讓CallContext實現跨線程傳播,可以調用CallContext的靜態方法ILogicalSetData,或讓上下文類實現ILogicalThreadAffinative 接口。
線程本地存儲
線程池可能不會釋放使用過的線程,導致多次執行之間可能共享數據(可以每次執行前重置線程本地存儲的數據)。
for (var i = 0; i < 10; i++)
{
Thread.Sleep(10);
Task.Run(() =>
{
var slot = Thread.GetNamedDataSlot("test");
if (slot == null)
{
Thread.AllocateNamedDataSlot("test");
}
if (Thread.GetData(slot) == null)
{
Thread.SetData(slot, DateTime.Now.Millisecond);
}
Console.WriteLine(Thread.CurrentThread.ManagedThreadId + ":" + Thread.GetData(slot));
});
}
結果
調用上下文
每次執行的數據是完全隔離的,非常符合我們的期望。但是,如果我們期望調用期間又開啟了一個子線程,如何讓子線程訪問父線程的數據呢?這就需要使用到:“邏輯調用上下文”。
Console.WriteLine("測試:CallContext.SetData");
for (var i = 0; i < 10; i++)
{
Thread.Sleep(10);
Task.Run(() =>
{
if (CallContext.GetData("test") == null)
{
CallContext.SetData("test", DateTime.Now.Millisecond);
}
Console.WriteLine(Thread.CurrentThread.ManagedThreadId + ":" + CallContext.GetData("test"));
});
}
結果
每次執行的數據是完全隔離的,非常符合我們的期望。
邏輯調用上下文
如果我們期望調用期間又開啟了一個子線程,如何讓子線程訪問父線程的數據呢?這就需要使用到:“邏輯調用上下文”。
注意 ExecutionContext.SuppressFlow(); 和ExecutionContext.RestoreFlow();它們分別能阻止傳播和重置傳播,默認是允許傳播的。
Console.WriteLine("測試:CallContext.SetData");
Task.Run(() =>
{
CallContext.SetData("test", "段光偉");
Console.WriteLine(Thread.CurrentThread.ManagedThreadId + ":" + CallContext.GetData("test"));
Task.Run(() =>
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId + ":" + CallContext.GetData("test"));
});
});
Thread.Sleep(100);
Console.WriteLine("測試:CallContext.LogicalSetData");
Task.Run(() =>
{
CallContext.LogicalSetData("test", "段光偉");
Console.WriteLine(Thread.CurrentThread.ManagedThreadId + ":" + CallContext.LogicalGetData("test"));
Task.Run(() =>
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId + ":" + CallContext.LogicalGetData("test"));
});
ExecutionContext.SuppressFlow();
Task.Run(() =>
{
Console.WriteLine("SuppressFlow 之后:" + CallContext.LogicalGetData("test"));
});
ExecutionContext.RestoreFlow();
Task.Run(() =>
{
Console.WriteLine("RestoreFlow 之后:" + CallContext.LogicalGetData("test"));
});
});
輸出
四、Web中的CallContext
HttpContext.Current(包括Session)的存儲是基于當前線程的CallContext,在非請求處理線程(即其他線程)是無法獲取當前HttpContext的(不跨線程傳播)。
原文鏈接:https://www.cnblogs.com/springsnow/p/9434008.html
相關推薦
- 2024-07-15 GIT同步修改后的遠程分支
- 2022-10-10 Go?語言前綴樹實現敏感詞檢測_Golang
- 2022-04-06 用Python實現一個簡單的用戶系統_python
- 2023-09-18 element-plus 字體變色之cell-style
- 2023-10-15 遍歷 bitset 中為 true 的下標
- 2022-10-15 Nginx如何配置加密證書訪問實現_nginx
- 2023-04-29 Linux?grep?-q用法示例詳解_linux shell
- 2022-08-03 GoFrame?gmap遍歷hashmap?listmap?treemap使用技巧_Golang
- 最近更新
-
- 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同步修改后的遠程分支