如何保证对象在线程内唯一

背景:为了解决多线程竞用共享资源的问题,引入数据槽的概念,即将数据存放到线程的环境块中,使该数据只能单一线程访问.(属于线程空间上的开销)

  下面的三种方式是解决多线程竞用共享资源的通用方式:

  ①:AllocateNamedDataSlot命名槽位和AllocateDataSlot未命名槽位 解决线程竞用资源共享问题。

  (PS:在主线程上设置槽位,使该数据只能被主线程读取,其它线程无法访问)

复制代码
   private void button10_Click(object sender, EventArgs e)
        {
            #region 01-AllocateNamedDataSlot命名槽位
            {
                var d = Thread.AllocateNamedDataSlot("userName");
                //在主线程上设置槽位,使该数据只能被主线程读取,其它线程无法访问
                Thread.SetData(d, "ypf");
                //声明一个子线程
                var t1 = new Thread(() =>
                {
                    Console.WriteLine("子线程中读取数据:{0}", Thread.GetData(d));
                });
                t1.Start();

                //主线程中读取数据
                Console.WriteLine("主线程中读取数据:{0}", Thread.GetData(d));
            }

            #endregion

            #region 02-AllocateDataSlot未命名槽位
            {
                var d = Thread.AllocateDataSlot();
                //在主线程上设置槽位,使该数据只能被主线程读取,其它线程无法访问
                Thread.SetData(d, "ypf");
                //声明一个子线程
                var t1 = new Thread(() =>
                {
                    Console.WriteLine("子线程中读取数据:{0}", Thread.GetData(d));
                });
                t1.Start();

                //主线程中读取数据
                Console.WriteLine("主线程中读取数据:{0}", Thread.GetData(d));

            }
            #endregion

        }
复制代码

  ②:利用特性[ThreadStatic] 解决线程竞用资源共享问题

   (PS:在主线程中给ThreadStatic特性标注的变量赋值,则只有主线程能访问该变量,比数据槽性能好,使用方便)

  ③:利用ThreadLocal线程的本地存储, 解决线程竞用资源共享问题(线程可见性)

  (PS: 在主线程中声明ThreadLocal变量,并对其赋值,则只有主线程能访问该变量)

 

 

④ CallContext 线程内可见。当 CallContext 沿执行代码路径往返传播并且由该路径中的各个对象检查时,可将对象添加到其中。
使用场景:当对象需要线程内全局使用。Core不支持

CallContext.SetData(key, temp);
DbContext temp = CallContext.GetData(key) as DbContext;

 

 

四. 内存栅栏-线程共享资源

背景:当多个线程共享一个变量的时候,在Release模式的优化下,子线程会将共享变量加载的cup Cache中,导致主线程不能使用该变量而无法运行。

内存栅栏:变量在进入内存栅栏并读取时,变量的值已经写入完毕,离开栅栏时,变量的值被读取时也已经写入完毕

解决方案:

  ①:不要让多线程去操作同一个共享变量,从根本上解决这个问题。

  ②:利用MemoryBarrier方法进行处理,在此方法之前的内存写入都要及时从cpu cache中更新到 memory;在此方法之后的内存读取都要从memory中读取,而不是cpu cache。

  ③:利用VolatileRead/Write方法进行处理。

 

a = 0 , b = 0;
void fun1() {   
  a = 1;   
  b = 1;
}

void fun2() {  
  while (b == 0) continue;  
  assert(a == 1);
}

 如果 一个线程执行 fun1(),另一个线程执行 fun2时,当b=1,a一定等于 1吗,答案是 不一定,a还有可能是0  具体请看 https://zhuanlan.zhihu.com/p/125737864

 

 

复制代码
 1  private void button11_Click(object sender, EventArgs e)
 2         {
 3             #region 01-默认情况(Release模式主线程不能正常运行)
 4             //{
 5             //    var isStop = false;
 6             //    var t = new Thread(() =>
 7             //    {
 8             //        var isSuccess = false;
 9             //        while (!isStop)
10             //        {
11             //            isSuccess = !isSuccess;
12             //        }
13             //        Console.WriteLine("子线程执行成功");
14             //    });
15             //    t.Start();
16 
17             //    Thread.Sleep(1000);
18             //    isStop = true;
19 
20             //    t.Join();
21             //    Console.WriteLine("主线程执行结束");
22             //}
23             #endregion
24 
25             #region 02-MemoryBarrier解决共享变量(Release模式下可以正常运行)
26             //{
27             //    var isStop = false;
28             //    var t = new Thread(() =>
29             //    {
30             //        var isSuccess = false;
31             //        while (!isStop)
32             //        {
33             //            Thread.MemoryBarrier();
34 
35             //            isSuccess = !isSuccess;
36             //        }
37             //        Console.WriteLine("子线程执行成功");
38             //    });
39             //    t.Start();
40 
41             //    Thread.Sleep(1000);
42             //    isStop = true;
43 
44             //    t.Join();
45             //    Console.WriteLine("主线程执行结束");
46             //}
47             #endregion
48 
49             #region 03-VolatileRead解决共享变量(Release模式下可以正常运行)
50             {
51                 var isStop = 0;
52                 var t = new Thread(() =>
53                 {
54                     var isSuccess = false;
55                     while (isStop == 0)
56                     {
57                         Thread.VolatileRead(ref isStop);
58 
59                         isSuccess = !isSuccess;
60                     }
61                     Console.WriteLine("子线程执行成功");
62                 });
63                 t.Start();
64 
65                 Thread.Sleep(1000);
66                 isStop = 1;
67 
68                 t.Join();
69                 Console.WriteLine("主线程执行结束");
70             }
71             #endregion
72 
73 
74         }
复制代码

 

posted @ 2020-07-12 22:16  凯帝农垦  阅读(546)  评论(0编辑  收藏  举报