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


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

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

      ①: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         }
    复制代码
  • 相关阅读:
    Flutter实战(四)---LoadingDialog
    多重if结构
    Java类型转换
    Eclipse安装中文简体语言包
    flutter 上传图片 image_picker 的使用
    【Dart学习】--Dart之数字(num)相关方法总结
    Flutter TextField详解
    003——angular 组件 以及组件里面的模板
    001——Typescript 介绍 、Typescript 安 装、Typescript 开发工具
    002——Angular 目录结构分析、app.module.ts 详解、以及 Angular 中创建组件、组件 详解、 绑定数据
  • 原文地址:https://www.cnblogs.com/wwkk/p/13290309.html
Copyright © 2020-2023  润新知