【问题标题】:Lockless using InterlockedCompareExchange使用 InterlockedCompareExchange 无锁
【发布时间】:2011-08-17 07:40:50
【问题描述】:

我正在尝试使用互锁操作使以下代码片段无锁,知道如何翻译吗?

if (m_Ref == 0xFFFF)
    m_Ref = 1;
else
{
    if (++m_Ref == 1)
        CallSomething(); //

}

我在想类似的事情

if (InterlockedCompareExchange(&m_Ref, 1, 0xFFFF) != 0xFFFF))
{
    if (InterlockedIncrement(&m_Ref) == 1)
         CallSomething();
}

这有什么问题/种族吗?

【问题讨论】:

    标签: c++ multithreading synchronization interlocked


    【解决方案1】:

    这看起来是正确的,但每次你连续使用两个互锁操作时,你都会将自己暴露在ABA problem 之下。在这种情况下,一个线程无法将其从 0xFFFF 更改为 1(ICX 返回 !=0xFFFF),因此它继续前进并采用 if 分支并将其递增。在它运行 InterlockedIncrement 之前,另一个线程将 m_ref 更改回 0xFFFF 并且原始线程递增 0xFFFF。根据 m_ref 的类型/语义,效果会很谨慎,但肯定会很糟糕。

    您应该对 0xFFF 到 1 和 X 到 X+1 执行一次 ICX 操作,如果 ICX 丢失,请始终重试:

    volatile <type> m_ref;
    
    <type> ref, newRef, icxref;
    do
    {
       ref = m_ref;
       newRef = (0xFFFF == ref) ? 1 : ++ref;
       icxref = InterlockedCompareExchange (&m_ref, newRef, ref);
    } while (icxref != ref);
    if (newRef == 1 && ref != 0xFFFF)
    {
       DoSomething ();
    }
    

    【讨论】:

    • @Remus Rusanu - 谢谢,这很有意义
    • @GMan:内存映射与volatile有什么关系?
    • @Remus:这是 volatile 的唯一用途,它使读写行为成为可观察的行为,它与多线程无关。您将其用作InterlockedCompareExchange 的参数已经提供了可观察的行为。
    • @Gman:ref = m_ref; 怎么样?没有 volatile 装饰器,它没有 Acquire 语义。
    • @Remus:它没有 with volatile... 无论如何,编译器不能忽略该分配,因为它稍后会在库调用中使用。 (即,它具有可观察的行为。)
    【解决方案2】:

    是的,有一场比赛。另一个上下文可以在InterlockedCompareExchangeInterlockedIncrement 之间做很多事情

    【讨论】:

    • 谢谢,我也是这么想的,你知道如何解决这个问题吗?
    • @Suresh:不是真的,如果你想让它无锁的话。如果您可以更改为允许 0,则在来自 InterlockedIncrement 的返回值上使用 %
    • @Suresh 如果您不希望在实际运行的代码中出现太多竞争,您可以使用 InterlockedCompareExchange 作为一种乐观锁定机制。
    猜你喜欢
    • 1970-01-01
    • 2018-10-11
    • 2012-06-17
    • 2012-04-03
    • 1970-01-01
    • 1970-01-01
    • 2013-08-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多