我总是需要通过互斥/原子来保护变量吗?

问题描述 投票:0回答:2

假设我有很多线程和一个简单的、可简单复制的非数组(基本类型,如

float
uint16_t
等)变量,称为
flag
一个且只有一个的线程经常设置变量的值,而其他线程仅从中读取值并且不写入它。在这种情况下,我是否必须使变量原子化或通过互斥体保护它?我知道当多个线程写入变量时我必须保护该变量,但在我的情况下有必要这样做吗?是否依赖于平台?

c++ multithreading mutex atomic
2个回答
5
投票

编译器可以自由地对此进行优化:

while (x != 0) {
  // code it knows does not modify x, nor synchronize with other threads
}

进入

if (x!=0) {
  while (true) {
    // code it knows does not modify x
  }
}

即,检查

x
一次并从逻辑上假设它无法更改。

但是,如果

x
是原子的,则不允许它们执行此操作,因为每次读取都会隐式同步发生在其他线程写入变量之后。

一般来说,在线程 1 中读取变量而不同步在线程 2 中写入同一变量在当前 C++ 内存模型下是 UB。

这个 UB 可能是硬件问题,并且允许编译器以您可能认为有敌意的方式优化您的代码。

所以,是的,您需要告诉编译器您的读/写可能会涉及线程间通信。

有趣的是,您的代码今天可能可以在使用当前编译器的硬件上运行。然后几年后,无害的操作系统更新、编译器更新或硬件修订将使您的代码失败。更重要的是,这种失败的情况可能很少见,甚至可能发生在今天!

你无法证明你的代码是正确的,因为我可以证明你的代码是错误的。您无法证明编译器(假设它没有错误)将永远生成执行您要求的操作的有效程序集,因为标准表明您的代码表现出 UB。

您也许能够获取编译器特定运行的汇编程序输出,并在 CPU 制造商为这些指令提供的保证下证明生成的代码是正确的。但我看过 CPU 指令描述,祝你好运。


0
投票

TLDR: 如果任意两个或多个线程访问同一个变量且没有同步, 并且至少其中一个访问是写入,那么这是未定义的行为 (UB)。如果你的程序依赖于 UB,那么就没有任何保证。不可能明确说明该程序会做什么或不会做什么。即使它就像一个线程设置一个标志来告诉其他线程“是时候停止了”一样简单。如果没有同步,那就是 UB,任何人都不可能确定会发生什么。

© www.soinside.com 2019 - 2024. All rights reserved.