使用另一个racy变量保护racy变量是否安全?

时间:2014-11-19 19:04:38

标签: c++ multithreading caching x86 volatile

void foo(volatile int& a, volatile int& b, bool threadOne) {
    if (threadOne) {
        //EDIT: switched the following two lines
        b = 10;
        a = 5;
    } else {
        while(a == 0);
        cout << a << b;
    }
}

//somewhere else
int a = 0;
int b = 0;
std::thread th1(foo, a, b, true);
std::thread th2(foo, a, b, false);

这在x86上是否安全,即根据c ++标准和x86参考的每个合法交错指令都将打印“510”?我的假设是它实际上是安全的。

2 个答案:

答案 0 :(得分:5)

§1.10[intro.multithread](引用N4140):

  

6两个表达式评估冲突如果其中一个修改了a   内存位置(1.7)和另一个访问或修改它   记忆位置。

     

23如果

,两个动作可能并发      
      
  • 它们由不同的线程执行,或
  •   
  • 它们没有排序,至少有一个是由信号处理程序执行的。
  •   
     

如果程序包含两个,则程序的执行包含数据竞争   潜在的并发冲突行为,其中至少有一个是   不是原子的,也没有发生在另一个之外,除了   下面描述的信号处理程序的特殊情况。任何这样的数据竞争   导致未定义的行为。

您的一个线程从a读取,另一个线程从a读取,两者都不是原子的,并且这两个潜在的并发冲突操作都不会在另一个之前发生。因此,您有数据竞争和未定义的行为。

答案 1 :(得分:4)

tl; dr 不,不,不,一千次没有。

就C ++ 11标准而言,程序具有未定义的行为,因为它具有数据争用(a由并发执行读取和写入而不同步。所以不,它不会总是打印510. volatile对此没有任何影响,它并不意味着C#和Java中的volatile意味着什么。

我也怀疑(但不能充满信心地断言)独立地,x86芯片也可以破坏这个程序,尽管可能不是由于交错。然而,

  1. a的新值可能会在b的新值变为可见之前生效(因为没有内存栅栏阻止芯片重新排序写入)。
  2. 任一变量的新值可能永远不会对第二个线程可见。查看缓存一致性协议。
  3. 你正在玩火。没有充分理由启动,条件变量很便宜。如果预期的延迟足够小,条件变量便宜,那么自旋锁(具有适当的同步,即原子)将是便宜的。