zoukankan      html  css  js  c++  java
  • .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 }
    复制代码

    结果

    说明

    如果使用了线程池,最好不要使用这种存储机制了,因为线程池可能不会释放使用过的线程,导致多次执行之间可能共享数据(可以每次执行前重置线程本地存储的数据)。

    调用上下文返回目录

    代码

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

    结果

    说明

    由上图可以知道,每次执行的数据是完全隔离的,非常符合我们的期望。但是,如果我们期望调用期间又开启了一个子线程,如何让子线程访问父线程的数据呢?这就需要使用到:“逻辑调用上下文”。

    逻辑调用上下文返回目录

    代码

    复制代码
     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 容器自定义生命周期管理模型。

     
    分类: .NET
  • 相关阅读:
    02-02:springboot 整合filter
    02-01:springboot整合servlet开发
    01-SpringBoot项目:helloworld
    SpringBoot
    JavaScript面试题
    vue的生命周期
    小程序下的兼容性问题
    短短几行css代码实现滚动条效果
    Apache服务器的安装和配置
    闭包
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3449470.html
Copyright © 2011-2022 走看看