重新排序内存操作的简单示例

时间:2019-11-05 21:35:06

标签: c++ c multithreading thread-safety data-race

我试图编写一些代码来观察内存操作的重新排序。

在下面的示例中,我希望在执行set_values()时可以更改分配值的顺序。特别地,通知= 1可能在其余操作之前发生,但是即使在thousens迭代之后也没有发生。 我已经使用-O3优化来编译代码。 这是我指的YouTube资料:https://youtu.be/qlkMbxUbKfw?t=200

int a{0};
int b{0};
int c{0};
int notification{0};

void set_values()
{
    a = 1;
    b = 2;
    c = 3;
    notification = 1;
}

void calculate()
{
   while(notification != 1);
   a += b + c;
}

void reset()
{
    a = 0;
    b = 0;
    c = 0;
    notification = 0;
}

int main()
{
    a=6; //just to allow first iteration

    for(int i = 0 ; a == 6 ; i++)
    {
        reset();
        std::thread t1(calculate);
        std::thread t2(set_values);

        t1.join();
        t2.join();
        std::cout << "Iteration: " << i << ", " "a = " << a << std::endl;
    }
    return 0;
}

现在程序陷入无限循环。我希望在某些迭代中,set_values()函数中的指令顺序可以更改(由于现金内存的优化)。例如,notification = 1将在c = 3之前执行,这将触发execute()函数的执行,并给出a == 3满足终止循环并证明重新排序的条件

或者也许有人可以提供其他琐碎的代码示例来帮助观察内存操作的重新排序?

2 个答案:

答案 0 :(得分:0)

编译器确实可以在函数set_values中重新分配您的分配。但是,不需要这样做。在这种情况下,由于您要为所有四个变量分配常量,因此没有理由重新排序。

  

现在程序陷入无限循环。

这可能是因为while(notification != 1);将被优化为无限循环。

通过一些工作,我们可以找到一种方法,使编译器在其他语句之前对分配notify = 1重新排序,请参见https://godbolt.org/z/GY-pAw。 请注意,程序从标准输入读取x,这样做是为了强制编译器从内存位置读取。

我还使变量notification易变,因此while(notification != 1);并没有得到优化。

您可以在计算机上尝试该示例,使用在英特尔Sandy Bridge cpu上运行的g ++ 9.2和-O3,我一直能够使断言始终失败。

请注意,如果cpu本身彼此独立,则它们可以对命令进行重新排序,请参见https://en.wikipedia.org/wiki/Out-of-order_execution。但是,要始终如一地进行测试和复制,会有些棘手。

答案 1 :(得分:0)

您的编译器会以意想不到的方式进行优化,但由于违反了C ++内存模型的基本规则,因此可以这样做。

如果其中至少一个线程是写程序,则无法从多个线程访问内存位置。

要进行同步,请为变量使用std:mutex或使用std:atomic<int>代替int