考虑几个并发执行以下代码的线程:
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
中所述)或者这需要互锁 - {{1} }?
如果两者都可以,从性能视图来看哪个更好,假设几个核同时调用InterlockedExchange(&gf, 0)
的可能性很高?
多个线程定期执行此代码(当某些事件触发时,从几个地方执行)并且下一个线程在释放后再次进入关键区域非常重要。
答案 0 :(得分:2)
相关:Spinlock with XCHG解释了为什么您不需要xchg
在x86 asm中释放锁定,只是存储指令。
但是在C ++ 中,你需要比普通gf = 0;
变量上的普通long gf
更强大的东西。 C / C ++内存模型(对于正常变量而言,即使在编译强排序的x86时也是非常弱的有序,因为这对于优化是必不可少的。
您需要一个发布商店来正确释放锁,而不允许在关键部分中的操作通过gf=0
在编译时或运行时重新排序而泄漏出关键部分商店。 http://preshing.com/20120913/acquire-and-release-semantics/
由于您使用的是long gf
,而不是volatile long gf
,并且您没有使用编译器内存屏障,因此代码中的任何内容都不会阻止编译时重新排序。 (x86 asm存储具有发布语义,所以它只需要担心编译时重新排序。)http://preshing.com/20120625/memory-ordering-at-compile-time/
我们使用std::atomic<long> gf;
尽可能便宜地获得我们所需的一切,gf.store(0, std::memory_order_release);
atomic<long>
在支持InterlockedExchange
的每个平台上都是无锁的,AFAIK,所以你应该可以混合搭配。 (或者只是使用gf.exchange()
来锁定。如果滚动自己的锁定,请记住在等待锁定时应该循环执行只读操作+ _mm_pause()
,不要用xchg
或lock cmpxchg
锤击,可能会延迟解锁。请参阅Locks around memory manipulation via inline assembly。
这是Why is integer assignment on a naturally aligned variable atomic on x86?中需要atomic<>
警告的情况之一,以确保编译器在需要的时间/地点实际执行存储。
答案 1 :(得分:-1)
NSW : 100.0
NT : 70.0
QLD : 99.0
SA : 84.0
VIC : 98.0
WA : 89.0
就足够了。没有必要使用锁定操作,因为没有其他线程可以更改其值。
顺便说一下,我会用bts而不是cmpxchg来获取锁。我不确定它是否会对性能产生任何影响,但它更简单。