C++原子抢占安全吗
Are C++ atomics preemption safe?
根据我对原子的理解,它们是特殊的汇编指令,可以保证SMP系统中的两个处理器不能同时写入同一内存区域。例如,在PowerPC中,原子增量看起来像:
retry:
lwarx r4, 0, r3 // Read integer from RAM location r3 into r4, placing reservation.
addi r4, r4, 1 // Add 1 to r4.
stwcx. r4, 0, r3 // Attempt to store incremented value back to RAM.
bne- retry // If the store failed (unlikely), retry.
然而,这并不能保护这四条指令不被一个中断和另一个正在调度的任务抢占。为了防止抢占,你需要在输入代码之前进行中断锁定。
从我对C++原子的了解来看,如果需要的话,它们似乎会强制执行锁。所以我的第一个问题是
- C++标准是否保证在原子操作期间不会发生抢占?如果是,我在标准中的哪里可以找到这个
我在英特尔电脑上检查了atomic<int>::is_always_lock_free
,结果是true
。在我对上述汇编块的假设下,这让我感到困惑。在深入研究英特尔汇编(我不熟悉)后,我发现lock xadd DWORD PTR [rdx], eax
正在发生。所以我的问题是
- 某些体系结构是否提供原子相关指令,以保证不进行预处理?还是我的理解错了
最后我想知道compare_exchange_weak
和compare_exchange_strong
语义-
- 区别在于重试机制还是其他原因
编辑:看完答案后,我对还有一件事感到好奇
- 原子成员函数操作
fetch_add
、operator++
等,它们是强还是弱
这类似于这个问题:std::atomic中的任何内容都是免费等待的?
以下是锁定自由和等待自由的一些定义(均取自维基百科):
如果程序线程运行足够长的时间时,至少有一个线程取得了进展,则算法是无锁。
如果每个操作都有算法在操作完成前要执行的步骤数的限制,则算法是无等待。
带有重试循环的代码是无锁的:线程只需要在存储失败时执行重试,但这意味着在此期间必须更新了值,因此其他线程必须已经取得了进展。
关于锁自由度,线程是否可以在原子操作的中间被抢占并不重要。
某些操作可以转换为单个原子操作,在这种情况下,此操作是等待空闲,因此不能在中途抢占。然而,哪些操作实际上是免费等待的,这取决于编译器和目标体系结构(如我在引用的SO问题中的回答所述)。
关于compare_exchange_weak
和compare_exchange_strong
之间的差异,弱版本可能会错误地失败,即即使比较实际上是真的,它也可能失败。这可能发生在具有LL/SC的体系结构上。假设我们使用compare_exchange_weak
更新一些具有期望值A的变量。LL从变量加载值A,在执行SC之前,变量将更改为B然后返回A。因此,即使变量包含与以前相同的值,对B的中间更改也会导致SC(因此compare_exchange_weak
)失败。compare_exchange_strong
不能错误地失败,但为了实现这一点,它必须在具有LL/SC的体系结构上使用重试循环。
我不完全确定你所说的CCD_;强或弱";。fetch_add
不能失败-它只是通过添加提供的值来执行某个变量的原子更新,并返回变量的旧值。这是否可以转换为单个指令(如在Intel上),或转换为使用LL/SC(Power)或CAS(Sparc)的重试循环,取决于目标体系结构。无论哪种方式,都可以保证变量得到正确更新。
C++标准是否保证在原子操作期间不会发生抢占?如果是,我在标准中的哪里可以找到这个?
不,不是。由于代码真的无法判断这种情况是否发生(根据情况,它与原子操作之前或之后的抢占无法区分),因此没有理由这样做。
某些体系结构是否提供保证不抢占的原子相关指令?还是我的理解错了?
没有意义,因为操作无论如何都必须看起来是原子的,所以在观察到的行为中,期间的抢占总是与之前或之后的抢占相同。如果您编写的代码能够看到原子操作期间的抢占会导致与之前的抢占或之后的抢占不同的可观察效果,那么该平台就会崩溃,因为操作的行为不是原子性的。
- 从不同线程使用int64的不同字节安全吗
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 虚拟决赛作为安全
- 获取日期异步信号安全吗?如果在信号处理程序中使用,它会导致死锁吗
- 如何将元素添加到数组的线程安全函数?
- C++中的线程安全删除
- 通过网络、跨平台传递std::变体是否安全
- 在std::thread中,joinable()然后join()线程安全吗
- 使用std::istream::peek()总是安全的吗
- 从值小于256的uint16到uint8的Endian安全转换
- 在c++队列中使用pop和visit实现线程安全
- 在类型和包装器之间reinterpret_cast是否安全<Type>?
- 以线程安全的方式调用"QQuickPaintedItem::updateImage(const QImage&image)"(no QThread)
- 全局变量 多读取器 一个写入器多线程安全?
- 安全到标准:移动会员?
- AcquireCredentialsHandleA() 返回 PFX 文件的0x8009030e(安全包中没有可用的凭据
- 共享队列的线程安全
- boost::文件系统::recursive_directory_iterator多线程安全
- 跨 DLL 边界访问虚拟方法是否安全/可能?
- C++动态安全 2D 交错阵列