在C++中使用多线程时,是否可以读取半写的、损坏的原始变量?
Is it possible to read half-written, corrupt primitive variable when using multithreading in C++?
如果在一条线程上我将写入一个原始类型的变量,例如int
,而另一个线程将读取它,是否有可能读取部分修改的数据,就像更复杂的数据类型一样?
如果是,那么我唯一的救援是atomic
还是mutex
,或者是否有性能开销较低的解决方案?
在理论上(以及现实生活中),是的。您必须同步对由不同线程读取和写入的变量的访问。
一些硬件架构可能不需要同步(在这种情况下,一个有能力的编译器应该删除它),但一些架构有非常宽松的保证(如DEC Alpha - 和其他),并且非常需要同步。
要获得跨嵌入的可移植且可预测的结果,您必须同步对变量的访问。可能会发生部分读取/写入(通常在晚上 3 点在您最重要的客户那里,当每个人都在度假时)。
更不用说缺乏同步的事实,您的程序包含数据竞赛,而数据竞赛根据定义是未定义的行为。一旦你的程序包含UB(任何地方),你的编译器就不再对它可能生成的代码(对于你的所有程序)有任何限制。因此,即使硬件保证它是安全的,编译器也可能利用理论上的UB的存在来执行优化,这些优化会破坏你的程序 - 不一定是你预期会破坏的地方。
根据标准 yes 的措辞:从多个线程访问同一个非atomic
对象,其中至少一个访问是写入,是未定义的行为。因此,您不仅可以看到"半写"或"损坏"的原始数据,还可能出现其他事情,例如不相关数据的损坏,无限循环,您的计算机变得有知觉并建造时间机器并穿越到过去以说服您的祖先永远不要从事会导致您出生的活动,分段故障,USB端口排放的有毒气体, 等。
在实践中,我不知道在现代架构上如何写入基元类型的对齐值时会发生这种情况,除了硬件本身不支持的"宽"基元类型(例如,某些多插槽 AMD 机器无法以原子方式写入对齐的 128 位值)。编译器通常会发出全宽加载和存储,所以我不知道一个现实的情况,例如编译器将一个 32 位存储转换为两个 16 位存储,尽管它完全在其权限范围内。
这并不意味着你应该这样做:编译器可以做出其他可能绊倒你的假设:例如,值永远不会改变,因此根本不需要重新读取。更重要的是,您可能想要的是与关键位置的std::memory_order_relaxed
std::atomic
配合,这些位置通常对性能的影响为零或几乎为零。不过,这是一个锋利的工具。
- 理解boost::asio-async_read在无需读取内容时的行为
- 为什么会发生堆损坏
- 使用新行和不使用新行读取文件
- 在C++中使用多线程时,是否可以读取半写的、损坏的原始变量?
- 获取无效或损坏的文件LNK1107:尝试链接 OpenSceneGraph 教程.dll时无法0x378读取
- 读取()和写入()到返回损坏值的套接字
- 尝试读取或写入受保护的内存.这通常表示其他内存已损坏.在C++Dll中
- 通过fstream读取文件时,存储在变量中的损坏数据..为什么?
- 已尝试读取或写入受保护的内存.这通常表示其他内存已损坏 DllImport
- 错误 1 错误 LNK1107:文件无效或损坏:无法读取0x2B0
- 访问冲突读取位置.堆损坏.提升线程
- 读取文件时堆损坏
- C++.NET包装程序:试图读取或写入受保护的内存.这通常表示其他内存已损坏
- 从文件读取时字符串已损坏
- 附加信息:试图读取或写入受保护的内存.这通常表明其他内存已损坏
- 一个字节一个字节地读取文件,会产生损坏的数据
- .obj:致命错误LNK1107:无效或损坏的文件:无法读取0x6592
- PostgreSQL c++ Api获取错误LNK1107:无效或损坏的文件:无法在0x2E8读取
- 读取/写入 PPM 图像C++,新图像损坏
- 在读取/写入文件时双重免费或损坏