2016-01-20 70 views
2

假设有一个由主线程控制的布尔标志(keep_running)。另一个线程无限循环直到该标志变为假。应该布尔标志总是原子?

int main() { 
    bool keep_running(true); 
    std::thread run([](bool &keep_running) 
    { 
     while(keep_running) 
     { 
      // do work 
     } 
    }, std::ref(keep_running)); 

    // do other work 
    keep_running = false; 
    run.join(); 
    return 0; 
} 

该标志应该是原子吗?

std::atomic<bool> keep_running 

我想,这可能发生在非原子版本最差的将是标志被正确的执行

while(keep_running) 

当时间设定。在这种情况下,循环会继续运行一次(不是严格需要的)迭代。但就我而言,这是可以接受的。

上面的代码可能是错误的吗?

编辑:

我在这个性能方面的原因大多是兴趣(和没有错误)。因此,对循环中的标志使用std :: atomic是否会对性能产生负面影响?

+1

在老版本的C++中,你会使用'volatile' - 没有它,就有可能从另一个线程看不到从一个线程写入变量。但是,因为在C++ 11中有'atomic',你应该使用它来防止相同的问题。 –

+0

当然它应该是原子的。这给你正确的语义。你为什么要做其他事情? –

+0

我在想,我在旗帜周围也不需要记忆障碍,也不需要关心指令重新排序。因此,我认为我可以摆脱std :: atomic来获得性能。我试图澄清,通过编辑问题。但是我没有意识到变量可能会卡在寄存器中,正如Tsyvarev在他的回答中所指出的那样。 –

回答

6

这只是C++ 11标准,它禁止(标记为未定义行为)并发访问非原子变量。

所以你需要声明这个变量原子。

请注意,使用keep_running.load(std::memory_order_relaxed)作为读取值,使用keep_running.store(true, std::memory_order_relaxed)作为写入值将消除任何额外的性能成本,因此导致的代码将会像没有原子的代码一样快。


我想,这可能发生在非原子版本最差的将是标志被正确的时间设置。

可能发生的情况是,在您的线程中,变量将被存储到寄存器中并且永不会被重新加载(所以线程永远不会被停止)。如果没有atomic或其他特殊类型和修饰符,编译器可以这样做,而且它确实如此。

+1

我会对此表示赞同,但认为“可能发生的最糟糕的是变量将被存储到您的线程中的寄存器中,并且不会被重新加载”是误导性的。就UB而言,可能发生的最糟糕的事情是什么。 Te编译器可以做任何响应它的任何事情,包括程序中任何一点的失败。请添加一个这样的记录,这将是一个很好的答案。 – Vality

+0

这篇文章以关于UB的概念开始,它被标准禁止*。最后,我没有必要在信息部分重复这一点。但是我已经删除了“最糟糕的”字样,所以它只是可能的*坏*情况之一,而不是唯一的情况。 – Tsyvarev

+0

谢谢,这是完美的,我不是说它应该重复,只是它不会让OP认为这是唯一可能发生的“最糟糕”似乎+1的不良影响 – Vality