如果一个线程设置已清零数组中的所有奇数位,另一个线程设置所有偶数位,那么内存中会包含什么?
简短回答 #
除非更新是原子的,否则最终值并不确定。在 C 和 C++ 中,未同步地并发写入同一存储位置会形成数据竞争,因此程序具有未定义行为。
缓存一致性会保持不同核心之间缓存行的一致,但不会将 read-modify-write 序列变成单个不可分割的操作。如果两个线程使用普通存储操作更新同一字节或字中的不同位,其中一个更新可能覆盖另一个更新,因为两个线程都可能读取相同的旧值,在本地修改后,再写回一个过时的副本。
为什么缓存行无法解决竞争 #
一致性协议以缓存行为粒度运行。这会影响性能,因为访问同一缓存行的两个线程会反复转移其所有权。
但这不会影响正确性,因为竞争发生在缓存协议之上:
- 线程 A 读取共享字。
- 线程 B 读取同一个字。
- A 设置自己的位并写回。
- B 基于过时的值设置自己的位并写回。
对于它所写入的字节,最后一次写入会生效,因此更早的位更新可能会消失。
结果稳定的情况 #
如果每次更新都是原子的按位操作,结果就会变得确定:
c
atomic_fetch_or_explicit(&word, mask, memory_order_relaxed);在这种情况下,硬件和语言运行时会将对共享字的修改串行化,因此最终值会成为所有更新的按位 OR 结果。
当每个线程写入互不重叠的字节、字或缓存行时,也会得到相同的结果。重要的不是“奇数位与偶数位”;重要的是两个写入是否可能在机器以原子方式更新的存储单元上发生重叠。
实践规则 #
- 对于紧凑型位集合,使用原子的 read-modify-write 操作。
- 对于高并发的计数器或标志,将数据分区,使线程不会共享同一缓存行。
- 对于普通的性能优化,请记住,即使逻辑数据互不重叠,false sharing 也会降低吞吐量。
评论
在 GitHub 上阅读或评论(发表评论需要登录)