退出关键区域

时间:2018-05-07 19:37:58

标签: multithreading x86 locking x86-64 mutual-exclusion

考虑几个并发执行以下代码的线程:

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)的可能性很高?

多个线程定期执行此代码(当某些事件触发时,从几个地方执行)并且下一个线程在释放后再次进入关键区域非常重要。

2 个答案:

答案 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(),不要用xchglock 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来获取锁。我不确定它是否会对性能产生任何影响,但它更简单。