[英]Is is worth to declare a size_t as std::atomic if used across 2 threads?
我有一个size_t
变量,它由std::thread
更新并由另一个std::thread
读取。
我知道我可以互斥保护读取和写入。 但是,如果我将size_t
设为std::atomic<size_t>
,它会是一样的还是有益的?
是的,这是值得的。 事实上,如果多个线程使用同一个变量并且至少有一个正在写入该变量,则必须使用std::atomic
或同步对非原子的访问。 不遵守此规则是数据竞争未定义行为。
根据您对std::size_t
的使用,编译器可以假设非原子和其他非同步变量不会从其他线程更改并相应地优化代码。 这可能会导致 Bad Things™ 发生。
我通常的例子是一个使用非原子布尔值的循环:
// make keepRunning an std::atomic<bool> to avoid endless loop
bool keepRunning {true};
unsigned number = 0;
void stop()
{
keepRunning = false;
}
void loop()
{
while(keepRunning) {
number += 1;
}
}
在启用优化的情况下编译此代码时,GCC 和 Clang 都只会检查keepRunning
一次,然后开始无限循环。 有关生成的汇编器输出,请参阅https://godbolt.org/z/GYMiLE 。
即他们将其优化为if (keepRunning) infinite_loop;
,将负载提升到环路之外。 因为它是非原子的,所以他们可以假设没有其他线程可以编写它。 有关同一问题的更详细信息,请参阅多线程程序卡在优化模式但在 -O0 中正常运行。
请注意,此示例仅在循环体足够简单时才显示错误。 然而,未定义的行为仍然存在,应该通过使用 std::atomic 或同步来避免。
在这种情况下,您可以将std::atomic<bool>
与std::memory_order_relaxed
因为您不需要任何同步或排序 wrt。 写入或读取线程中的其他操作。 这将为您提供原子性(无撕裂)并假设该值可以异步更改,而无需让编译器使用任何 asm 屏障指令来创建更多排序 wrt。 其他操作。
因此,在没有任何同步的情况下使用原子是可能且安全的,甚至不需要像 seq_cst 或获取/释放加载和存储那样在写入器和读取器之间创建同步。 您可以使用此同步来安全地共享非原子变量或数组,例如,当指针为非 NULL 时读取器读取的atomic<int*> buffer
。
但是如果只有原子变量本身是共享的,你可以让读者读取当前值,而不关心同步。 如果您不需要重新读取短循环的每次迭代,每次函数调用只需一次,您可能希望将其读入本地临时文件。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.