線程本地存儲
using System; using System.Threading; using System.Threading.Tasks; namespace ConsoleAppTest { class Program { static void Main(string[] args) { ThreadDataSlotTest.Test(); } } /// <summary> /// 線程本地存儲 /// </summary> class ThreadDataSlotTest { public static void Test() { 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.ReadLine(); } } }
如果使用了線程池,最好不要使用這種存儲機制了,因為線程池可能不會釋放使用過的線程,導致多次執行之間可能共享數據(可以每次執行前重置線程本地存儲的數據)。
調用上下文
using System; using System.Runtime.Remoting.Messaging; using System.Threading; using System.Threading.Tasks; namespace ConsoleAppTest { class Program { static void Main(string[] args) { CallContextTest.Test(); } } /// <summary> /// 調用上下文 /// </summary> class CallContextTest { public static void Test() { if (CallContext.GetData("test") == null) { CallContext.SetData("test", "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")); }); } Console.ReadLine(); } } }
由上圖可以知道,每次執行的數據是完全隔離的,非常符合我們的期望。但是,如果我們期望調用期間又開啟了一個子線程,如何讓子線程訪問父線程的數據呢?這就需要使用到:“邏輯調用上下文”。
邏輯調用上下文
using System; using System.Runtime.Remoting.Messaging; using System.Threading; using System.Threading.Tasks; namespace ConsoleAppTest { class Program { static void Main(string[] args) { ExecutionContextTest.Test(); } } /// <summary> /// 調用上下文 /// </summary> class ExecutionContextTest { public static void Test() { Console.WriteLine("測試:CallContext.SetData"); Task.Run(() => { CallContext.SetData("test", "wolf"); 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", "wolf"); 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")); }); }); Console.ReadLine(); } } }
注意 ExecutionContext.SuppressFlow(); 和 ExecutionContext.RestoreFlow();,它們分別能阻止傳播和重置傳播,默認是允許傳播的。