一个线程编写一个线程是未定义的行为volatile
根据 C11,当另一个线程读取该变量时。volatile
访问也不会相对于其他访问进行排序。你要atomic_store_explicit(&head, new_value, memory_order_release)
在作家和atomic_load_explicit(&head, memory_order_acquire)
在阅读器中创建 acq/rel 同步,并强制编译器在存储之前使存储到结构中可见head
这向读者表明有新数据。
(tail
对于读取器线程来说是私有的,因此写入器没有机制可以在写入更多数据之前等待读取器看到新数据。因此,从技术上讲,如果写入器线程在读取器仍在读取时再次写入,则结构内容可能会出现竞争。所以结构也应该是_Atomic
).
您可能需要一个序列锁,其中写入者更新序列号,而读取者之前检查它and复制出变量后。 https://en.wikipedia.org/wiki/Seqlock这使您可以在读取器复制数据时写入器正在更新的极少数情况下进行检测并重试。
这对于只写/只读情况非常有用,特别是当您不需要担心读者错过更新时。
请参阅我在 C++11 中对 SeqLock 的尝试:使用 32 位原子实现 64 位原子计数器并且如何使用c++11原子库实现seqlock锁
And GCC 使用“memory_order_seq_cst”在负载上重新排序。这是允许的吗?显示另一个示例(此示例会导致 gcc 错误)。
将它们从 C++11 std::atomic 移植到C11 标准原子应该很简单。确保使用atomic_store_explicit
,因为默认内存排序为普通atomic_store
is memory_order_seq_cst
哪个更慢。
实际上,您无能为力,无法加快作家使其商店在全球范围内可见的速度。 CPU 核心已经尽快将存储从其存储缓冲区提交到其 L1d(遵守 x86 内存模型的限制,该模型不允许 StoreStore 重新排序)。
在 Xeon 上,请参阅CPU何时将storebuffer中的值刷新到L1 Cache?有关不同侦听模式及其对单个套接字内核心间延迟的影响的一些信息。
多核上的缓存是一致的,使用MESI来保持一致性。
阅读器在原子变量上旋转等待可能是你能做的最好的事情,使用_mm_pause()
在自旋循环内部,以避免退出自旋循环时清除内存顺序错误推测管道。
您也不希望在写入过程中醒来并重试。您可能希望将 seq-lock 计数器与数据放在同一缓存行中,因此这些存储有望合并到写入核心的存储缓冲区中。