[英]Multithread share 2 variable problem with nonlock
我有一个关于多线程共享变量问题的问题。 这两个变量是这样的:
{
void* a;
uint64_t b;
}
只有一个线程可以修改这两个变量,其他线程会频繁读取这两个变量。 我想一次更改 a 和 b,其他线程会一起看到更改(请参阅新值 a 和新值 b)。 因为很多线程会频繁读取这两个变量,所以不想加锁,想问有没有办法结合change a和b操作,让它像原子操作一样? 就像使用记忆栅栏一样,它会起作用吗? 谢谢!
您正在寻找SeqLock 。
它非常适合这种用例,尤其是对于不经常更改的数据。 (例如,像一个由定时器中断更新的时间变量,到处读取。)
SeqLock 的优势包括完美的读取端扩展(读取器不需要获得任何缓存行的独占所有权,它们是真正的只读而不仅仅是无锁的),因此任何数量的读取器都可以随心所欲地读取彼此零争用。 缺点是偶尔重试,如果读者碰巧在错误的时间尝试阅读。 这种情况很少见,而且当作者还没有写完东西时就不会发生这种情况。
所以读者并不是完全没有等待,事实上,如果作者在错误的时间睡觉,读者就会陷入重试,直到它再次醒来! 所以总体而言,该算法甚至不是无锁或无阻塞的。 但是非常常见的快速路径只是从与数据相同的高速缓存行中额外读取两次,以及读取器中 LoadLoad 排序所需的任何内容。 如果自上次读取后没有写入,则负载都可能是 L1d 缓存命中。
唯一更好的是,如果您有高效的 16 字节原子存储和加载,例如带有 AVX 的 Intel(但还不是 AMD)CPU,如果您的编译器/libatomic 将它用于std::atomic<struct_16bytes>
的 16 字节加载x86-64 lock cmpxchg16b
。 (实际上,尽管大多数 AMD CPU 也具有原子 16 字节加载/存储,但只有 Intel 正式在其手册中指出 AVX 功能位意味着对齐 128 位加载/存储的原子性,例如movaps
,所以编译器可以安全地开始使用它。)
或者我认为 AArch64 保证 ARMv8.4 中普通stp
/ ldp
的 16 字节原子性。
但是如果没有这些硬件特性,以及利用它们的编译器+选项,16 字节加载通常会被实现为原子 RMW,这意味着每个读取器都拥有缓存行的独占所有权。 这意味着读取与其他读取竞争,而不是缓存线保持共享状态,在每个正在读取它的内核的缓存中都是热的。
就像使用记忆栅栏一样,它会起作用吗?
不,内存栅栏不能创建原子性(将多个操作粘合到一个更大的事务中),只能在操作之间创建排序。
尽管您可以说 SeqLock 背后的想法是仔细排序写入和读取(wrt. to sequence variable),以便检测撕裂的读取并在发生时重试。 所以,是的,障碍对此很重要。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.