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

Posted 积少成多

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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(); 和 ExecutionContext.RestoreFlow();,它们分别能阻止传播和重置传播,默认是允许传播的。

 

以上是关于C# 线程本地存储 调用上下文 逻辑调用上下文的主要内容,如果未能解决你的问题,请参考以下文章

CallContext

C#如何回到主线程,如何在委托指定线程执行

EF上下文对象线程内唯一性与优化

调用更新本地存储返回未定义

锁内的新线程 - c#

在服务调用的线程中获取上下文