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();,它們分別能阻止傳播和重置傳播,默認是允許傳播的。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章