考虑多个线程同时执行以下代码:
long gf = 0;// global variable or class member
//...
if (InterlockedCompareExchange(&gf, 1, 0)==0) // lock cmpxchg
{
// some exclusive code - must not execute in concurrent
gf = 0; // this is ok ? or need
//InterlockedExchange(&gf, 0); // [lock] xchg
}
将上面的代码视为类似 C 的伪代码,它将或多或少地直接转换为汇编语言,而无需对编译器优化(例如重新排序和存储消除)做出通常的让步。
所以在某个线程独占获取标志之后gf
- 要退出临界区,写一个零就足够了(如gf = 0
)或者这是否需要互锁 -InterlockedExchange(&gf, 0)
?
如果两者都可以,从性能角度来看哪个更好,假设多个核心同时调用的可能性很大InterlockedCompareExchange(&gf, 1, 0)
?
多个线程定期执行此代码(当某些事件触发时,从多个位置执行),并且下一个线程在释放后尽快再次进入临界区域非常重要。
有关的:带 XCHG 的自旋锁 https://stackoverflow.com/questions/36731166/spinlock-with-xchg解释了为什么你don't need xchg
要释放 x86 asm 中的锁,只需一条存储指令。
But in C++,你需要比普通的更强大的东西gf = 0;
在平原上long gf
多变的。C / C++ 内存模型(对于普通变量)的顺序非常弱,即使是针对强顺序 x86 进行编译也是如此,因为这对于优化至关重要。
您需要一个释放存储才能正确释放锁,不允许通过在编译时或运行时重新排序来允许关键部分中的操作泄漏出关键部分gf=0
店铺。http://preshing.com/20120913/acquire-and-release-semantics/ http://preshing.com/20120913/acquire-and-release-semantics/.
既然你正在使用long gf
, not volatile long gf
,并且您没有使用编译器内存屏障,代码中的任何内容都不会阻止编译时重新排序。 (x86 asm 存储具有发布语义,因此我们只需担心编译时重新排序。)http://preshing.com/20120625/memory-ordering-at-compile-time/ http://preshing.com/20120625/memory-ordering-at-compile-time/
我们使用尽可能便宜的方式获得我们需要的一切std::atomic<long> gf;
and gf.store(0, std::memory_order_release);
atomic<long>
在每个支持的平台上都是无锁的InterlockedExchange
,据我所知,所以你应该可以混合搭配。 (或者只是使用gf.exchange()
拿锁。如果滚动您自己的锁,请记住您应该循环执行只读操作 +_mm_pause()
等待锁时,请勿敲打xchg
or lock cmpxchg
并可能延迟解锁。看通过内联汇编锁定内存操作 https://stackoverflow.com/questions/37241553/locks-around-memory-manipulation-via-inline-assembly/37246263#37246263.
这是警告的情况之一为什么自然对齐变量的整数赋值在 x86 上是原子的? https://stackoverflow.com/questions/36624881/why-is-integer-assignment-on-a-naturally-aligned-variable-atomic-on-x86你需要的atomic<>
确保编译器确实在您需要的地方/时间进行存储。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)