为什么不稳定,MemoryBarrier并不prevent操作重新排序? [英] Why volatile and MemoryBarrier do not prevent operations reordering?

查看:123
本文介绍了为什么不稳定,MemoryBarrier并不prevent操作重新排序?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我理解的意思挥发和MemoryBarrier比正确的程序如下从来没有要能表现出任何结果。

它捉住我每次运行时写操作的重新排序。没关系,如果我在调试或发布运行。这也没关系,如果我运行它作为32位或64位的应用程序。

为什么会发生?

 使用系统;
    使用的System.Threading;
    使用System.Threading.Tasks;

    命名空间触发器
    {
        类节目
        {
            //声明这些变量的波动应指示编译器
            //刷新从寄存器中的所有高速缓存到内存中。
            静态挥发INT一个;
            静态挥发INT B:

            //跟踪一些迭代,它采取了检测操作重新排序。
            静态长迭代= 0;

            静态对象更衣室=新的对象();

            //表示操作重新排序暂时还没有发现。
            静态挥发布尔continueTrying = TRUE;

            //表示检查方法应继续下去。
            静态挥发布尔continueChecking = TRUE;

            静态无效的主要(字串[] args)
            {
                //重新开始测试,直到能够赶上重新排序。
                而(continueTrying)
                {
                    迭代++;
                    VAR检查=新任务(检查);
                    VAR writter =新的任务(写);
                    锁(更衣室)
                    {
                        continueChecking = TRUE;
                        checker.Start();

                    }
                    writter.Start();
                    checker.Wait();
                    writter.Wait();
                }
                Console.ReadKey();
            }

            静态无效的write()
            {
                //写被锁定,直到主开始检查()方法。
                锁(更衣室)
                {
                    //使用内存屏障应该prevent调度研究重新排序。
                    一个= 1;
                    Thread.MemoryBarrier();
                    B = 10;
                    Thread.MemoryBarrier();
                    B = 20;
                    Thread.MemoryBarrier();
                    一个= 2;

                    //停止旋转的检查方法。
                    continueChecking = FALSE;
                }
            }

            静态无效的检查()
            {
                //产品转速直到发现操作重新排序或Write方法停止。
                而(continueChecking)
                {
                    INT TEMPA =一个;
                    b INT tempB =;

                    如果(tempB == 10安培;&安培; TEMPA == 2)
                    {
                        continueTrying = FALSE;
                        Console.WriteLine(中招当a = {0}和b = {1},TEMPA,tempB);
                        Console.WriteLine(在+ +迭代反复。);
                        打破;
                    }
                }
            }
        }
    }
 

解决方案

我不认为这是重新排序。

这一块code是根本就不是线程安全的:

 而(continueChecking)
 {
     INT TEMPA =一个;
     b INT tempB =;
     ...
 

我觉得这种情况是可能的:

  1. INT TEMPA =一; 执行与最后一个循环(一== 2)的值
  2. 有一个上下文切换到写线程
  3. B = 10 和循环停止
  4. 有一个上下文切换到检查线程
  5. INT tempB = B; 用b ==执行10

我注意到调用MemoryBarrier()加强本方案的机会。大概是因为它们会导致更多的上下文切换。

If I understand meaning of volatile and MemoryBarrier correctly than the program below has never to be able to show any result.

It catches reordering of write operations every time I run it. It does not matter if I run it in Debug or Release. It also does not matter if I run it as 32bit or 64bit application.

Why does it happen?

    using System;
    using System.Threading;
    using System.Threading.Tasks;

    namespace FlipFlop
    {
        class Program
        {
            //Declaring these variables as volatile should instruct compiler to 
            //flush all caches from registers into the memory.
            static volatile int a;
            static volatile int b;

            //Track a number of iteration that it took to detect operation reordering.
            static long iterations = 0;

            static object locker = new object();

            //Indicates that operation reordering is not found yet.
            static volatile bool continueTrying = true;

            //Indicates that Check method should continue.
            static volatile bool continueChecking = true;

            static void Main(string[] args)
            {
                //Restarting test until able to catch reordering.
                while (continueTrying)
                {
                    iterations++;
                    var checker = new Task(Check);
                    var writter = new Task(Write);
                    lock (locker)
                    {
                        continueChecking = true;
                        checker.Start();

                    }
                    writter.Start();
                    checker.Wait();
                    writter.Wait();
                }
                Console.ReadKey();
            }

            static void Write()
            {
                //Writing is locked until Main will start Check() method.
                lock (locker)
                {
                    //Using memory barrier should prevent opration reordering.
                    a = 1;
                    Thread.MemoryBarrier();
                    b = 10;
                    Thread.MemoryBarrier();
                    b = 20;
                    Thread.MemoryBarrier();
                    a = 2;

                    //Stops spinning in the Check method.
                    continueChecking = false;
                }
            }

            static void Check()
            {
                //Spins until finds operation reordering or stopped by Write method.
                while (continueChecking)
                {
                    int tempA = a;
                    int tempB = b;

                    if (tempB == 10 && tempA == 2)
                    {
                        continueTrying = false;
                        Console.WriteLine("Caught when a = {0} and b = {1}", tempA, tempB);
                        Console.WriteLine("In " + iterations + " iterations.");
                        break;
                    }
                }
            }
        }
    }

解决方案

I don't think this is re-ordering.

This piece of code is simply not thread-safe:

 while (continueChecking)
 {
     int tempA = a;
     int tempB = b;
     ...

I think this scenario is possible:

  1. int tempA = a; executes with the values of the last loop (a == 2)
  2. There is a context switch to the Write thread
  3. b = 10 and the loop stops
  4. There is a context switch to the Check thread
  5. int tempB = b; executes with b == 10

I notice that the calls to MemoryBarrier() enhance the chances of this scenario. Probably because they cause more context-switching.

这篇关于为什么不稳定,MemoryBarrier并不prevent操作重新排序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆