- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
语境
我正在写一个线程安全的 protothread/coroutine library在 C++ 中,我正在使用原子学使任务切换无锁。我希望它尽可能高效。我对原子和无锁编程有一般的了解,但我没有足够的专业知识来优化我的代码。我做了很多研究,但很难找到我的具体问题的答案:不同内存顺序下不同原子操作的传播延迟/可见性是多少?
当前假设
我读到对内存的更改是从其他线程传播的,以这种方式它们可能变得可见:
我不确定这种延迟的可见性和不一致的传播是否仅适用于非原子读取,或者也适用于原子读取,这可能取决于使用的内存顺序。当我在 x86 机器上开发时,我无法在弱序系统上测试行为。
无论操作类型和使用的内存顺序如何,所有原子读取是否总是读取最新值?
我很确定所有读取-修改-写入 (RMW) 操作总是读取任何线程写入的最新值,无论使用的内存顺序如何。 顺序一致操作似乎也是如此,但前提是对变量的所有其他修改也顺序一致。据说两者都很慢,这对我的任务不利。如果不是所有原子读取都获得最新值,那么根据我目前的理解,我将不得不使用 RMW 操作来读取原子变量的最新值,或者在 while 循环中使用原子读取。
写入的传播(忽略副作用)是否取决于内存顺序和使用的原子操作?
(只有当上一个问题的答案是并非所有原子读取总是读取最新值时,这个问题才有意义。请仔细阅读,我在这里不问副作用的可见性和传播。我只关心原子变量本身的值。) 这意味着根据用于修改原子变量的操作,可以保证任何后续原子读取都接收到最近的值多变的。因此,我必须在保证始终读取最新值的操作之间做出选择,或者使用宽松的原子读取,以及这种特殊的写入操作,以保证对其他原子操作的修改的即时可见性。
最佳答案
首先,让我们摆脱房间里的大象:使用atomic
在您的代码中并不能保证无锁实现。 atomic
只是无锁实现的插入者。 is_lock_free()
会告诉您对于 C++ 实现和您正在使用的底层类型,它是否真的是无锁的。
术语“最新”在多线程世界中是非常模糊的。因为对于一个可能被操作系统休眠的线程来说,什么是“最新的”,对于另一个事件的线程来说可能不再是最新的。
std::atomic
唯一的保证是通过确保 R, M and RMW 来防止赛车条件在一个线程中对一个原子执行的操作以原子方式执行,没有任何中断,并且所有其他线程都看到之前的值或之后的值,但看不到中间值。所以atomic
通过在同一原子对象上的并发操作之间创建顺序来同步线程。
您需要将每个线程视为一个具有自己时间的平行宇宙,并且不知道平行宇宙中的时间。就像在量子物理学中一样,你在一个线程中唯一能知道的关于另一个线程的就是你能观察到的(即宇宙之间“之前发生过”的关系)。
这意味着你不应该把多线程时间想象成所有线程都有一个绝对的“最新”时间。您需要将时间视为相对于其他线程的时间。这就是为什么原子不创建绝对最新的,而只确保原子将具有的连续状态的顺序排序。
传播不依赖于内存顺序,也不依赖于执行的原子操作。 memory_order是关于围绕原子操作的非原子变量的顺序约束,这些原子操作看起来像栅栏。关于这是如何工作的最好的解释当然是 Herb Sutters presentation ,如果您正在处理多线程优化,那绝对值得花一个半小时。
虽然特定的 C++ 实现可能会以影响传播的方式实现某些原子操作,但您不能依赖任何此类观察,因为无法保证传播以相同的方式工作编译器的下一个版本或另一个 CPU 架构上的另一个编译器。
当 designing lock-free algorithms ,很容易读取原子变量以获得最新状态。但是,尽管这种只读访问是原子的,但紧随其后的操作却不是。因此,以下指令可能会假定一个已经过时的状态(例如,因为线程在原子读取后立即进入休眠状态)。
取if(my_atomic_variable<10)
并假设您读到 9。假设您处于最佳可能世界中,并且 9 将是所有并发线程设置的绝对最新值。将其值与 <10
进行比较不是原子的,所以当比较成功并且if
分支机构,my_atomic_variable
可能已经有一个新值 10。而且无论传播速度有多快,即使读取保证始终获得最新值,这种问题也可能发生。我什至没有提到 ABA problem然而。
读取的唯一好处是避免数据竞争和 UB。但是如果你想跨线程同步决策/ Action ,你需要使用RMW,比如compare-and-swap (例如 atomic_compare_exchange_strong
)以便原子操作的排序产生可预测的结果。
关于c++ - 原子操作传播/可见性(原子负载与原子 RMW 负载),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55079321/
在 J. Sorin 的书“内存一致性和缓存一致性入门”中,我找到了关于 SC 模型中 RMW 优化的下一段: More aggressive implementations of RMWs leve
我记得在优化 x86 的速度时,通常要避免读-修改-写指令。也就是说,你应该避免像 add [rsi], 10 这样的东西。 ,这增加了存储在 rsi 中的内存位置.建议通常是将其拆分为读取-修改指令
C++ 标准规定,原子上的 RMW(读-修改-写)操作将对原子变量的最新值进行操作。因此,当从多个线程并发执行时,对这些操作使用 memory_order_relaxed 不会影响 RMW 操作。 我
假设我对thread0中的两个原子对象进行了两次轻松的修改,每个对象一个,然后让thread1观察到thread0中的第二个修改。现在,如果没有内存围栏,如果thread1尝试加载第一个对象,它可能不
语境 我正在写一个线程安全的 protothread/coroutine library在 C++ 中,我正在使用原子学使任务切换无锁。我希望它尽可能高效。我对原子和无锁编程有一般的了解,但我没有足够
硬件提供原子指令,如测试和设置、比较和交换、加载链接存储条件。这些是特权指令吗?也就是说,只有操作系统才能执行它们(因此需要系统调用)吗? 我以为他们没有特权,可以在用户空间调用。但是http://f
众所周知,由于使用了写缓冲区,x86 架构没有实现顺序一致性内存模型,因此可以进行 store->load 重新排序(可以提交稍后的加载,而较早的存储仍然驻留在写缓冲区中等待提交) L1缓存)。 在
我是一名优秀的程序员,十分优秀!