C# 线程本地存储 调用上下文 逻辑调用上下文

线程本地存储

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(); 和 xecutionContext.RestoreFlow();,它们分别能阻止传播和重置传播,默认是允许传播的。

时间: 2024-10-07 07:50:39

C# 线程本地存储 调用上下文 逻辑调用上下文的相关文章

[并发并行]_[C/C++]_[使用线程本地存储Thread Local Storage(TLS)-win32和pthread比较]

场景: 1.  需要统计某个线程的对象上创建的个数. 2. 当创建的堆空间需要根据线程需要创建和结束时销毁时. 3. 因为范围是线程只能看到自己的存储数据,所以不需要临界区或互斥量来维护自己的堆内存. 加入如果用全局std::map实现,那么必须在put和get时加锁,这是很损耗资源的. 4. 可以用在维护一个连接,比如socket,database连接. 说明: 1. Java也有自己的线程本地存储ThreadLocal 2. pthread的win32版本: http://sourcewar

windows TLS (线程本地存储)

windows TLS (线程本地存储) 一.TLS简述和分类 我们知道在一个进程中,所有线程是共享同一个地址空间的.所以,如果一个变量是全局的或者是静态的,那么所有线程访问的是同一份,如果某一个线程对其进行了修改,也就会影响到其他所有的线程.不过我们可能并不希望这样,所以更多的推荐用基于堆栈的自动变量或函数参数来访问数据,因为基于堆栈的变量总是和特定的线程相联系的. 不过如果某些时候(比如可能是特定设计的dll),我们就是需要依赖全局变量或者静态变量,那有没有办法保证在多线程程序中能访问而不互

java线程 在其他对象上同步、线程本地存储ThreadLocal:thinking in java4 21.3.6

package org.rui.thread.concurrency; /** * 在其他对象上同步 * synchronized 块必须给定一个在其上进行同步的对象,并且最合理的方式是,使用其方法正在被调用的当前对象 * :synchronized(this), 在 这种方式中,如果获得了synchronized块上的锁, * 那么该对象其他的synchronized方法和临界区就不能被调用了. * 因此,如果在this上同步,临界区的效果就会直接缩小在同步的范围内. * * 有时必须在另一个

线程本地存储及实现原理

本文是<go调度器源代码情景分析>系列 第一章 预备知识的第十小节,也是预备知识的最后一小节. 线程本地存储又叫线程局部存储,其英文为Thread Local Storage,简称TLS,看似一个很高大上的东西,其实就是线程私有的全局变量而已. 有过多线程编程的读者一定知道,普通的全局变量在多线程中是共享的,一个线程对其进行了修改,所有线程都可以看到这个修改,而线程私有的全局变量与普通全局变量不同,线程私有全局变量是线程的私有财产,每个线程都有自己的一份副本,某个线程对其所做的修改只会修改到自

聊聊Linux中的线程本地存储(1)——什么是TLS

从本篇开始进入另一个话题:线程本地存储(Thread Local Storage),在介绍这个概念前先说说变量和多线程的相关知识. 多线程下的变量模型 在单线程模型下,变量定义有两个维度,那就是在何处定义,以及它的修饰属性(static, extern,auto,register等).extern属性表示声明一个变量 ,与定义无关,在此不作讨论:而register是将变量优化成寄存器里面,不作讨论.与变量定义相关的修饰属性就只有auto和static了.这两个维度可以得到变量的类型,以及它们的行

[并发并行]_[C/C++]_[使用线程本地存储Thread Local Storage(TLS)调用复制文件接口的案例]

使用场景: 1. 在复制文件时,一般都是一个线程调用一个接口复制文件,这时候需要缓存数据,如果每个文件都需要创建独立的缓存,那么内存碎片是很大的. 如果创建一个static的内存区,当多线程调用同一个接口时,多个线程同时使用同一个static缓存会造成数据污染.最好的办法是这个缓存只对这个线程可见, 当线程创建时创建缓存区,当线程结束时销毁缓存区. 2. 代码里有注释: test.cpp #include <iostream> #include "pthread.h" #i

PE线程本地存储

1.静态tls将变量定义在PE文件内部. 使用.tls节存储 .tls节中包含: 初始化数据 用于每个线程初始化和终止的回调函数 TLS索引 2.代码访问tls数据时经过的步骤: (1) 链接时, 链接器设置tls目录中的AddressOfIndex字段. 该字段指向一个位置,该位置保存了程序用到的tls索引 (2) 创建线程时, 将TEB的地址放入fs寄存器来传递tls数组地址. teb+0x2c处字段指向tls数组 (3) 将tls索引值保存到AddressOfIndex字段指向的位置 (4

TLS 线程本地存储

TLS (Thread Local Storage) XP系统上的Portable executable不支持动态加载. https://reverseengineering.stackexchange.com/questions/14171/thread-local-storage-access-on-windows-xp/14186#14186 http://www.cnblogs.com/wuyuan2011woaini/p/6124385.html http://www.cnblogs.

.Net - 线程本地变量(存储)的使用

关于C#多线程的文章,大部分都在讨论线程的开始与停止或者是多线程同步问题.多线程同步就是在不同线程中访问同一个变量或共享资源,众所周知在不使用线程同步的机制下,由于竞争的存在会使某些线程产生脏读或者是覆盖其它线程已写入的值(各种混乱). 而另外一种情况就是多线程时我们想让每个线程所访问的变量只属于各自线程自身所有,这就是所谓的线程本地变量. 线程本地变量不是用于解决共享变量的问题的,不是为了协调线程同步而存在,而是为了方便每个线程处理自己的状态而引入的一个机制,理解这点对正确使用线程本来变量至关