.NET:线程本地存储、调用上下文、逻辑调用上下文

.NET:线程本地存储、调用上下文、逻辑调用上下文

背景返回目录

在多线程环境,如果需要将实例的生命周期控制在某个操作的执行期间,该如何设计?经典的思路是这样的:作为参数向调用栈传递,如: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 }
复制代码

结果

.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.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 }
复制代码

结果

.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.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 }
复制代码

输出

.NET:线程本地存储、调用上下文、逻辑调用上下文

说明

注意 ExecutionContext.SuppressFlow(); 和 xecutionContext.RestoreFlow();,它们分别能阻止传播和重置传播,默认是允许传播的。

备注返回目录

最常见的使用场景就是:为 Ioc 容器自定义生命周期管理模型。

 

 
分类:  .NET

你可能感兴趣的:(.net)