在多線程環境,若是須要將實例的生命週期控制在某個操做的執行期間,該如何設計?經典的思路是這樣的:做爲參數向調用棧傳遞,如:CommandExecuteContext、HttpContext等。好在不少平臺都提供線程本地存儲這種東西,下面介紹一下 .NET 提供的三種機制。多線程
代碼測試
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading; 6 using System.Threading.Tasks; 7 using System.Runtime.Remoting; 8 9 namespace ExecutionContextStudy 10 { 11 class ThreadDataSlotTest 12 { 13 public static void Test() 14 { 15 for (var i = 0; i < 10; i++) 16 { 17 Thread.Sleep(10); 18 19 Task.Run(() => 20 { 21 var slot = Thread.GetNamedDataSlot("test"); 22 if (slot == null) 23 { 24 Thread.AllocateNamedDataSlot("test"); 25 } 26 27 if (Thread.GetData(slot) == null) 28 { 29 Thread.SetData(slot, DateTime.Now.Millisecond); 30 } 31 32 Console.WriteLine(Thread.CurrentThread.ManagedThreadId + ":" + Thread.GetData(slot)); 33 }); 34 } 35 36 Console.ReadLine(); 37 } 38 } 39 }
結果spa
說明線程
若是使用了線程池,最好不要使用這種存儲機制了,由於線程池可能不會釋放使用過的線程,致使屢次執行之間可能共享數據(能夠每次執行前重置線程本地存儲的數據)。設計
代碼code
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading; 6 using System.Threading.Tasks; 7 using System.Runtime.Remoting.Messaging; 8 9 namespace ExecutionContextStudy 10 { 11 class CallContextTest 12 { 13 public static void Test() 14 { 15 Console.WriteLine("測試:CallContext.SetData"); 16 for (var i = 0; i < 10; i++) 17 { 18 Thread.Sleep(10); 19 20 Task.Run(() => 21 { 22 if (CallContext.GetData("test") == null) 23 { 24 CallContext.SetData("test", DateTime.Now.Millisecond); 25 } 26 27 Console.WriteLine(Thread.CurrentThread.ManagedThreadId + ":" + CallContext.GetData("test")); 28 }); 29 } 30 31 Console.ReadLine(); 32 } 33 } 34 }
結果blog
說明生命週期
由上圖能夠知道,每次執行的數據是徹底隔離的,很是符合咱們的指望。可是,若是咱們指望調用期間又開啓了一個子線程,如何讓子線程訪問父線程的數據呢?這就須要使用到:「邏輯調用上下文」。it
代碼io
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading; 6 using System.Threading.Tasks; 7 using System.Runtime.Remoting.Messaging; 8 9 namespace ExecutionContextStudy 10 { 11 class ExecutionContextTest 12 { 13 public static void Test() 14 { 15 Console.WriteLine("測試:CallContext.SetData"); 16 Task.Run(() => 17 { 18 CallContext.SetData("test", "段光偉"); 19 Console.WriteLine(Thread.CurrentThread.ManagedThreadId + ":" + CallContext.GetData("test")); 20 21 Task.Run(() => 22 { 23 Console.WriteLine(Thread.CurrentThread.ManagedThreadId + ":" + CallContext.GetData("test")); 24 }); 25 }); 26 27 Thread.Sleep(100); 28 29 Console.WriteLine("測試:CallContext.LogicalSetData"); 30 Task.Run(() => 31 { 32 CallContext.LogicalSetData("test", "段光偉"); 33 Console.WriteLine(Thread.CurrentThread.ManagedThreadId + ":" + CallContext.LogicalGetData("test")); 34 35 Task.Run(() => 36 { 37 Console.WriteLine(Thread.CurrentThread.ManagedThreadId + ":" + CallContext.LogicalGetData("test")); 38 }); 39 40 ExecutionContext.SuppressFlow(); 41 Task.Run(() => 42 { 43 Console.WriteLine("SuppressFlow 以後:" + CallContext.LogicalGetData("test")); 44 }); 45 46 ExecutionContext.RestoreFlow(); 47 Task.Run(() => 48 { 49 Console.WriteLine("RestoreFlow 以後:" + CallContext.LogicalGetData("test")); 50 }); 51 }); 52 53 Console.ReadLine(); 54 } 55 } 56 }
輸出
說明
注意 ExecutionContext.SuppressFlow(); 和 xecutionContext.RestoreFlow();,它們分別能阻止傳播和重置傳播,默認是容許傳播的。
最多見的使用場景就是:爲 Ioc 容器自定義生命週期管理模型。