如果在一个线程上我将写一个原始类型的变量,例如hideRow(rowNum)
,而另一个线程正在读取它,则可能会读取部分修改的数据,例如更复杂的数据类型?
如果是,那么我唯一的救助对象是int
或atomic
,还是有一个性能开销较小的解决方案?
答案 0 :(得分:8)
理论上(以及现实生活中),是的。您必须同步访问不同线程读取和写入的变量。
某些硬件体系结构可能不需要同步(在这种情况下,胜任的编译器应将其删除),但是某些体系结构的保证非常宽松(例如DEC Alpha等),并且同步非常非常需要。
要获得跨计划的可移植且可预测的结果,必须必须同步对变量的访问。 可能会发生部分读取/写入(通常在每个人休假时晚上3点在您最重要的客户那里)。
更不用说缺乏同步的事实了,您的程序包含一个数据争用,并且数据争用的定义是未定义的行为。并且一旦您的程序包含UB(任意位置),您的编译器就不再可能生成任何代码(对于程序的 all )没有任何限制。因此,即使硬件保证它是安全的,编译器也可能会利用理论上的UB来执行优化,这将破坏您的程序-不一定是您希望破损发生的地方。
答案 1 :(得分:7)
按标准的措辞是:从多个线程(至少一次访问是写操作)访问同一非atomic
对象是未定义的行为。因此,您不仅可以看到“半写的”或“损坏的”原始数据,还可以进行其他操作,例如不相关数据的损坏,无限循环,计算机变得有感知力,构建时间机器并旅行过去说服您的祖先不要从事会导致您出生,分割错误,USB端口散发有毒气体等的活动。
在实践中,我不知道在写对齐类型的原始类型(而不是“宽”原始类型)时,在现代架构上会如何发生硬件本身不支持(例如,某些多插槽AMD机器无法原子地写入对齐的128位值)。编译器通常会发出全角的加载和存储,因此我不知道现实的情况,例如,您可能会被绊倒。编译器将一个32位存储转换为两个16位存储,尽管它完全在其权限范围内。
这并不意味着您应该这样做:编译器可以做出可能使您绊倒的 other 个假设:例如,该值永不更改,因此不需要重新设置-读完。更重要的是,您可能想要的是std::atomic
与std::memory_order_relaxed
在关键位置协同工作,而关键位置通常对性能的影响为零或几乎为零。不过,这是一个非常有用的工具。