c#線程本地存儲數據

環境:

  • window 10
  • .netcore 3.1.1
  • vs 16.4.5

說明:
要達到的效果是,每一個線程都有一個單獨的容器用來存儲數據。實現的方法有三種:線程內的數據槽、ThreadStatic特性、ThreadLocal<T>對象

一、線程內的數據槽

線程中的數據槽(LocalDataStoreSlot)是一種給線程存儲數據的容器,一個槽只能存一個數據,一個線程可以有很多個槽。可以給槽命名,已命名的槽和線程建立了關聯,你可以在任何地方從線程中獲取命名的數據槽(GetNamedDataSlot("name-slot"))。注意:數據存儲在槽內的,所以線程存取數據都是要指定具體槽的SetData(slot, "xiaoming")GetData(slot)。還有,槽是對所有線程都有效的,只不過不同的線程的同一個槽內存取的是不同的數據。
線程中的數據槽有兩種,未命名的和已命名的:

  • 未命名的數據槽:
    只能通過AllocateDataSlot()方法獲取,獲取後如果想使用,那麼所有線程都必須能引用到獲取的槽。
  • 命名數據槽:
    可以通過GetNamedDataSlot(“slotname”)或AllocateNamedDataSlot(“slot-name”)獲取,獲取後這個槽本身就和線程建立了關聯(以槽名稱作爲紐帶),這樣當線程需要時直接通過GetNamedDataSlot獲取就行了,當需要清除關聯時使用Thread.FreeNamedDataSlot(“names-slot”)方法清除這個關聯,注意這個只是清除關聯,不會清除已獲取的槽中的數據,所以即使清除關聯後,你只要有這個slot的引用,也是可以進行正常的存取數據的。如果不清楚關聯的話AllocateNamedDataSlot方法發現已經有命名的槽的話就會報異常,而GetNamedDataSlot方法不會。

下面看看數據槽的使用示例:

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            //Test1();
            //Test2();
            //Test3();
            Console.WriteLine("ok");
            Console.ReadLine();
        }

        /// <summary>
        /// 測試命名線程槽的GetNamedDataSlot方法
        /// </summary>
        private static void Test3()
        {
            LocalDataStoreSlot slot = Thread.GetNamedDataSlot("names-slot");
            var thread = new Thread(() =>
            {
                var slot2 = Thread.GetNamedDataSlot("names-slot");
                Console.WriteLine("slot==slot2=>" + (slot == slot2).ToString());
            });
            thread.Start();
            Thread.FreeNamedDataSlot("names-slot");
            var slot3 = Thread.AllocateNamedDataSlot("names-slot");
            var slot4 = Thread.AllocateNamedDataSlot("names-slot");
            Console.WriteLine("slot3==slot4 -> " + (slot3 == slot4));
        }

        /// <summary>
        /// 測試命名的線程槽
        /// </summary>
        private static void Test2()
        {
            LocalDataStoreSlot slot = Thread.AllocateNamedDataSlot("names-slot");
            var thread = new Thread(() =>
            {
                Thread.SetData(slot, "xiaoming");
                var data = Thread.GetData(slot);
                Console.WriteLine("work-thread-slot:" + data);
                Thread.FreeNamedDataSlot("names-slot");
                data = Thread.GetData(slot);
                var slot2 = Thread.GetNamedDataSlot("names-slot");
                data = Thread.GetData(slot2);
                Console.WriteLine("slot==slot2" + (slot == slot2).ToString());
                Console.WriteLine("work-thread-slot:" + data);
                Thread.SetData(slot, "xiaohong");
                Console.WriteLine("work-thread-slot:" + Thread.GetData(slot));
            });
            thread.Start();
            Thread.Sleep(3000);
            var data2 = Thread.GetData(slot);
            Console.WriteLine("main-thread-slot:" + data2);
        }

        /// <summary>
        /// 測試未命名的線程槽
        /// </summary>
        private static void Test1()
        {
            LocalDataStoreSlot slot = Thread.AllocateDataSlot();
            Thread.SetData(slot, "xiaoming");
            var data = Thread.GetData(slot);
            var thread = new Thread(() =>
              {
                  var data2 = Thread.GetData(slot);
                  Console.WriteLine("work-thread-slot:" + data2);
              });
            thread.Start();
            Console.WriteLine("main-thread-slot:" + data);
        }
    }
}

二、ThreadStatic

這種方法是在靜態字段上添加[ThreadStatic]特性,然後每個線程訪問時都是不同的值。
注意:這要求字段必須是靜態的,如果不是靜態的那麼加上[ThreadStatic]特性也沒用,因爲字段不是特性的話,它就屬於一個實例對象,沒辦法在一個實例對象內根據線程進行隔離。
如果這個字段是靜態的,那麼這個字段是屬於類的,它對所有的線程都一樣,此時再給它加上[ThreadStatic]特性就會把這個靜態字段進行線程隔離。
下面來看實驗代碼:

[ThreadStatic]
private static string name = string.Empty;
static void Main(string[] args)
{
	 var thread = new Thread(() =>
	   {
	       name = "xiaoming";
	       Console.WriteLine("work name=" + name);
	   });
	 thread.Start();
     thread.Join();
     Console.WriteLine("main name=" + name);
     Console.WriteLine("ok");
     Console.ReadLine();
}

三、ThreadLocal

這是一個容器,提供了一個對象專門用來存儲和線程相關的對象,它和[ThreadStatic]最大的不同是:不再要求字段是靜態的。
具體使用方式如下:

class Program
{
    private ThreadLocal<string> username = new ThreadLocal<string>();
    static void Main(string[] args)
    {

        var pro = new Program();
        var thread = new Thread(() =>
        {
            pro.username.Value = "xiaoming";
            Console.WriteLine("worker username=" + pro.username.Value);
        });
        thread.Start();
        thread.Join();
        Console.WriteLine("worker username=" + pro.username.Value);
        Console.WriteLine("ok");
        Console.ReadLine();
    }
}
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        #region 測試ThreadStatic、ThreadLocal本地存儲
        /*
         * 測試ThreadStatic、ThreadLocal本地存儲
         */
        //[ThreadStatic]
        //private static string name = string.Empty;
        //static void Main(string[] args)
        //{
        //    ThreadLocal<string> userName = new ThreadLocal<string>();
        //    var thread = new Thread(() =>
        //      {
        //          userName.Value = "xiaohua";
        //          name = "xiaoming";
        //          Console.WriteLine("work name=" + name);
        //          Console.WriteLine("work userName=" + userName.Value);
        //      });
        //    thread.Start();
        //    thread.Join();
        //    Console.WriteLine("main name=" + name);
        //    Console.WriteLine("main userName=" + userName.Value);


        //    Console.WriteLine("ok");
        //    Console.ReadLine();
        //} 
        #endregion


        #region Debug和Releas模式的運行速度對比
        //public static void Main(string[] args)
        //{
        //    //for (int i = 0; i < 10000; i++)
        //    //{
        //    //    int res = new Random().Next(1, 100000);
        //    //    File.AppendAllText("c:\\temp\\ints.txt", res + "\r\n");
        //    //}
        //    //Release和Debug模式的區別
        //    var datas = File.ReadAllLines("c:\\temp\\ints.txt").Select(i => int.Parse(i)).ToList<int>();
        //    for (int i = 0; i < 5; i++)
        //    {
        //        var arr = new int[datas.Count];
        //        datas.CopyTo(arr);
        //        Stopwatch watch = new Stopwatch();
        //        watch.Start();
        //        Order(arr);
        //        watch.Stop();
        //        Console.WriteLine(watch.Elapsed);
        //    }
        //    Console.WriteLine("ok");
        //    Console.Read();
        //}

        //public static int[] Order(int[] datas)
        //{
        //    for (int i = 0; i < datas.Length; i++)
        //    {
        //        for (int j = i + 1; j < datas.Length; j++)
        //        {
        //            if (datas[i] < datas[j])
        //            {
        //                int t = datas[i];
        //                datas[i] = datas[j];
        //                datas[j] = t;
        //            }
        //        }
        //    }
        //    return datas;
        //} 
        #endregion
        public static void Main(string[] args)
        {
            var isStop = 0;
            var t = new Thread(() =>
              {
                  var isSuccess = true;
                  while (isStop == 0)
                  {

                      //Thread.VolatileRead(ref isStop);
                      isSuccess = !isSuccess;
                  }
              });
            t.Start();
            Thread.Sleep(1000);
            isStop = 1;
            t.Join();
            Console.WriteLine("ok");
            Console.ReadLine();


            //bool isStop = false;
            //var thread = new Thread(() =>
            //  {
            //      var isSuccess = false;
            //      while (!isStop)
            //      {
            //          //這個方法可以指示從內存中讀寫
            //          //Thread.MemoryBarrier();
            //          isSuccess = !isSuccess;
            //      }
            //  });
            //thread.Start();
            //Thread.Sleep(1000);
            //isStop = true;
            //thread.Join();
            //Console.WriteLine("ok");
            //Console.ReadLine();

            //下面不會出現bug
            //bool isStop = false;
            //var thread = new Thread(() =>
            //{
            //    while (true)
            //    {
            //        if (isStop) break;
            //    }
            //});
            //thread.Start();
            //Thread.Sleep(1000);
            //isStop = true;
            //thread.Join();
            //Console.WriteLine("ok");
            //Console.ReadLine();
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章