比较和交换C ++

时间:2015-01-13 10:24:00

标签: c++ multithreading multiprocessing inline-assembly lock-free

所以我们正在使用一个版本的boost,这个版本现在很老了,直到我需要升级 为我的代码用C ++进行原子CAS操作。 (我们还没有使用C ++ 0x)

我创建了以下cas函数:

inline uint32_t CAS(volatile uint32_t *mem, uint32_t with, uint32_t cmp)
{
    uint32_t prev = cmp;
    // This version by Mans Rullgard of Pathscale
    __asm__ __volatile__ ( "lock\n\t"
            "cmpxchg %2,%0"
            : "+m"(*mem), "+a"(prev)
              : "r"(with)
                : "cc");

    return prev;
}

我使用该功能的代码有点如下:

void myFunc(uint32_t &masterDeserialize )
{
    std::ostringstream debugStream;

    unsigned int tid = pthread_self();
    debugStream << "myFunc, threadId: " << tid << " masterDeserialize= " << masterDeserialize << " masterAddress = " << &masterDeserialize << std::endl;

    // memory fence
    __asm__ __volatile__ ("" ::: "memory");
    uint32_t retMaster = CAS(&masterDeserialize, 1, 0);
    debugStream << "After cas, threadid = " << tid << " retMaster = " << retMaster << " MasterDeserialize = " << masterDeserialize << " masterAddress = " << &masterDeserialize << std::endl;
    if(retMaster != 0) // not master deserializer.
    {
       debugStream << "getConfigurationRowField, threadId: " << tid << " NOT master.  retMaster = " << retMaster << std::endl;

       DO SOMETHING...
    }
    else
    {
        debugStream << "getConfigurationRowField, threadId: " << tid << " MASTER. retMaster = " << retMaster << std::endl;

        DO SOME LOGIC  

        // Signal we're done deserializing.
        masterDeserialize = 0;
    }
    std::cout << debugStream.str();
}

我对此代码的测试产生了10个线程,并通过相同的masterDeserialize 变量向所有线程发出信号。

这在大多数情况下运行良好,但每两千次 - 几万次测试迭代2个线程都可以进入获取MASTER锁的路径。

我不确定这是怎么可能的,或者如何避免它。

我在重置masterDeserialize之前尝试使用内存栅栏,认为cpu OOO可能有影响,但这对结果没有影响。

显然,这是在具有多个内核的机器上运行,并且它是在调试模式下编译的,因此GCC不应该重新排序执行以进行优化。

有关上述问题的任何建议吗?

编辑: 我尝试使用gcc原语而不是汇编代码,得到了相同的结果。

inline uint32_t CAS(volatile uint32_t *mem, uint32_t with, uint32_t cmp)
{
    return __sync_val_compare_and_swap(mem, cmp, with);
}

我在多核,多CPU机器上运行,但它是一台虚拟机,这种行为是否可能是由VM造成的?

1 个答案:

答案 0 :(得分:1)

理论上,不仅两个而且任意数量的线程都可以成为主人和#34;在这段代码中。问题是在完成后占用主路径的线程将masterDeserialize变量设置回0,从而可以获得&#34;获取&#34;再一次由一个可能很晚才到达CAS的线程(例如由于先发制人)。

修复实际上很简单 - 将第三个状态(例如值为2)添加到此标志以表示&#34; master已完成&#34;,并使用此状态(而不是初始状态0)在主人道路的尽头,表明他们的工作已经完成。因此,只有一个调用myFunc的线程可以看到0,这为您提供所需的保证。要重复使用该标志,您需要明确地将其重新初始化为0。