为什么volatile和MemoryBarrier不会阻止操作重新排序?

Den*_*nis 13 .net c# multithreading

如果我正确理解volatile和MemoryBarrier的含义,那么下面的程序永远无法显示任何结果.

它每次运行时都会捕获写操作的重新排序.如果我在Debug或Release中运行它并不重要.如果我将它作为32位或64位应用程序运行也没关系.

为什么会这样?

    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;
                    }
                }
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

Mar*_*ell 10

你不是正在打扫变量测试之间,这样(为所有,但第一)最初a2b20- Write任何事情.

Check可以获得(so is )的初始值,然后可以进入,直到更改为.atempA2Writeb10

现在Checkb(所以tempBIS 10).

瞧瞧.重新订购无需重新订购.

复位ab0运行之间,我希望它会自行消失.

编辑:确认; "按原样"我几乎立即得到了这个问题(<2000次迭代); 但通过添加:

while (continueTrying)
{
    a = b = 0; // reset <======= added this
Run Code Online (Sandbox Code Playgroud)

它然后循环任何时间没有任何问题.

或者作为一个流程:

Write                   A=  B=        Check

(except first run)      2   20
                                      int tempA = a;
a = 1;                  1   20
Thread.MemoryBarrier();
b = 10;                 1   10
                                      int tempB = b;
Run Code Online (Sandbox Code Playgroud)


Hen*_*man 3

我不认为这是重新排序。

这段代码根本就不是线程安全的:

 while (continueChecking)
 {
     int tempA = a;
     int tempB = b;
     ...
Run Code Online (Sandbox Code Playgroud)

我认为这种情况是可能的:

  1. int tempA = a;使用最后一个循环的值执行 (a == 2)
  2. Write 线程有一个上下文切换
  3. b = 10循环停止
  4. 有一个到 Check 线程的上下文切换
  5. int tempB = b;执行时 b == 10

我注意到对 MemoryBarrier() 的调用增加了出现这种情况的可能性。可能是因为它们会导致更多的上下文切换。