在C ++中使用多线程处理时,是否可以读取半写的损坏的原始变量?

3 c++ concurrency multithreading race-condition memory-corruption

如果在一个线程上我将写入原始类型的变量(例如)int,而另一个线程正在读取该变量,那么是否有可能像更复杂的数据类型一样读取部分修改的数据?

如果是的话,那么我唯一的解决方法是要么atomic或者mutex要么有没有一种性能开销较小的解决方案?

Jes*_*uhl 7

从理论上(以及现实生活中),是的。您必须同步访问不同线程读取和写入的变量。

某些硬件体系结构可能不需要同步(在这种情况下,合格的编译器应将其删除),但是某些体系结构的保证很松懈(例如DEC Alpha和其他),非常需要同步。

为了使跨计划的结果可移植且可预测,您必须同步访问变量。可能会进行部分读取/写入(通常在每个人度假时,晚上3点在您最重要的客户处)。

更不用说缺乏同步的事实,您的程序包含一个数据争用,并且根据定义,数据争用是未定义的行为。并且,一旦程序包含UB(在任何地方),编译器对其(对于您的所有程序)可能生成的代码将不再有任何限制。因此,即使硬件保证它是安全的,编译器也可能会利用理论上的UB来执行优化,这将破坏您的程序-不一定是您希望破损发生的地方。

  • @YanB。0)请参阅更新我的答案。1)取决于硬件和操作系统。2)还取决于编译器。大多数现代编译器会积极利用理论上的UB,并可能破坏您的程序。3)在一个好的编译器上,无论如何都会编译出不必要的同步,因此它不会花费您任何钱,而且可以节省您的工作。 (2认同)

Bee*_*ope 6

用标准的措辞是:atomic从多个线程(其中至少一个访问是写操作)访问同一非对象是未定义的行为。因此,您不仅可以看到“半写的”或“损坏的”原始数据,而且还可能发生其他事情,例如无关数据的损坏,无限循环,计算机变得有知觉,构建时间机器并穿越过去以说服您的祖先切勿进行会导致您出生,分割错误,USB端口散发有毒气体的活动。

实际上,我不知道在写原始类型的对齐值时这种情况在现代体系结构上会如何发生,而不是硬件本身不支持的“宽”原始类型(例如,某些多插槽AMD机器发生故障以原子方式写入对齐的128位值)。编译器通常会发出全角的加载和存储,因此我不知道现实的情况,例如,编译器将32位存储转换为两个16位存储,尽管这种情况非常适合它的权利

这并不意味着您应该这样做:编译器可以做出其他可能使您失望的假设:例如,该值永不更改,因此根本不需要重新读取它。更重要的是,您可能希望在关键位置std::atomic协同工作,std::memory_order_relaxed而这些关键位置通常对性能的影响为零或几乎为零。但是,这是一个锋利的工具。

  • “在实践中,当我编写原始类型的对齐值时,我不知道在现代体系结构上会如何发生”-我相当确定某些现代ARM芯片在这一领域有一些非常宽松的要求/保证,并且可能会给出令人惊讶的结果没有适当的同步。然后,在房间中总是有一个巨大的“ it's UB”大象,因此您的编译器可能会在* BIG时间*上使您费解,而与硬件无关。 (2认同)