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();
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章