• .NET 线程独享的全局数据 TLS


    线程和进程最大的一个区别就在于线程间可以共享数据和资源,而进程则充分地隔离。在很多场合,即使同一进程的多个线程之间拥有相同的内存空间,也需要在逻辑上为某些线程分配独享的数据。例如,在实际开发中往往会针对一些ORM如EF一类的上下文实体做线程内唯一实例的设置,这时就需要用到下面提到的技术。

    (1)线程本地存储(Thread Local Storage,TLS)

    很多时候,程序员可能会希望拥有线程内可见的变量,而不希望其他线程对其进行访问和修改(传统方式中的静态变量是对整个应用程序域可见的),这就需要用到TLS的概念。所谓的线程本地存储(TLS)是指存储在线程环境块内的一个结构,用来存放该线程内独享的数据。进程内的线程不能访问不属于自己的TLS,这就保证了TLS内的数据在线程内是全局共享的,而对于线程外确实不可见的。

    (2)定义和使用TLS变量

    在.NET中提供了下列连个方法来存取线程独享的数据,它们都定义在System.Threading.Thread类型中:

    ① object GetData(LocalDataStoreSlot slot)

    ② void SetData(LocalDataStoreSlot slot, object data)

    下面的代码示例则展示了这个机制的使用方法:

    class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine("开始测试数据插槽:");
                // 创建五个线程来同时运行,但是这里不适合用线程池,
                // 因为线程池内的线程会被反复使用导致线程ID一致
                for (int i = 0; i < 5; i++)
                {
                    Thread thread = new Thread(ThreadDataSlot.Work);
                    thread.Start();
                }
    
                Console.ReadKey();
            }
        }
    
        /// 
        /// 包含线程方法和数据插槽
        /// 
        public class ThreadDataSlot
        {
            // 分配一个数据插槽,注意插槽本身是全局可见的,因为这里的分配是在所有线程
            // 的TLS内创建数据块
            private static LocalDataStoreSlot localSlot = Thread.AllocateDataSlot();
    
            // 线程要执行的方法,操作数据插槽来存放数据
            public static void Work()
            {
                // 将线程ID注册到数据插槽中,一个应用程序内线程ID不会重复
                Thread.SetData(localSlot, Thread.CurrentThread.ManagedThreadId);
                // 查看一下刚刚插入的数据
                Console.WriteLine("线程{0}内的数据是:{1}",Thread.CurrentThread.ManagedThreadId.ToString(),Thread.GetData(localSlot).ToString());
                // 这里线程休眠1秒
                Thread.Sleep(1000);
                // 查看其他线程的运行是否干扰了当前线程数据插槽内的数据
                Console.WriteLine("线程{0}内的数据是:{1}", Thread.CurrentThread.ManagedThreadId.ToString(), Thread.GetData(localSlot).ToString());
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    在这里插入图片描述
    (3)ThreadStaticAttribute特性的使用

    除了使用上面说到的数据槽之外,我们还有另一种方式,即ThreadStaticAttribute特性。申明了该特性的变量,会被.NET作为线程独享的数据来使用。我们可以将其理解为一种被.NET封装了的TLS机制,本质上,它仍然使用了线程环境块来存放数据。

    下面的示例代码展示了ThreadStaticAttribute特性的使用:
    class Program
    {
    static void Main(string[] args)
    {
    Console.WriteLine(“开始测试数据插槽:”);
    // 创建五个线程来同时运行,但是这里不适合用线程池,
    // 因为线程池内的线程会被反复使用导致线程ID一致
    for (int i = 0; i < 5; i++)
    {
    Thread thread = new Thread(ThreadStatic.Work);
    thread.Start();
    }

            Console.ReadKey();
        }
    }
    
    /// 
    /// 包含线程静态数据
    /// 
    public class ThreadStatic
    {
        // 值类型的线程静态数据
        [ThreadStatic]
        private static int threadId = 0;
        // 引用类型的线程静态数据
        private static Ref refThreadId = new Ref();
    
        /// 
        /// 线程执行的方法,操作线程静态数据
        /// 
        public static void Work()
        {
            // 存储线程ID,一个应用程序域内线程ID不会重复
            threadId = Thread.CurrentThread.ManagedThreadId;
            refThreadId.Id = Thread.CurrentThread.ManagedThreadId;
            // 查看一下刚刚插入的数据
            Console.WriteLine("[线程{0}]:线程静态值变量:{1},线程静态引用变量:{2}", Thread.CurrentThread.ManagedThreadId.ToString(), threadId, refThreadId.Id.ToString());
            // 睡眠1s
            Thread.Sleep(1000);
            // 查看其他线程的运行是否干扰了当前线程静态数据
            Console.WriteLine("[线程{0}]:线程静态值变量:{1},线程静态引用变量:{2}", Thread.CurrentThread.ManagedThreadId.ToString(), threadId, refThreadId.Id.ToString());
        }
    }
    
    /// 
    /// 简单引用类型
    /// 
    public class Ref
    {
        private int id;
    
        public int Id
        {
            get
            {
                return id;
            }
            set
            {
                id = value;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    在这里插入图片描述

  • 相关阅读:
    云计算概论 --云管理机制
    C++文件服务器项目—FastDFS—1
    SSM实验室设备管理
    window10安裝mysql8.0.18
    跨越障碍:解决复杂网页数据提取的挑战
    一 企业信息化战略和实施
    27.4 Java集合之Map学习
    新建anaconda使用jupyter出现的一系列问题
    【MMDetection3D】MVXNet踩坑笔记
    es 读流程源码解析
  • 原文地址:https://blog.csdn.net/u013400314/article/details/126871493