c++ - C++中的比较和交换

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

所以我们使用的是目前相当旧的 bo​​ost 版本,在升级之前我需要 为我的代码在 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会有影响,但对结果没有影响。

很明显,它在多核机器上运行,并且是在 Debug模式下编译的,因此 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 以某种方式引起的?

最佳答案

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

修复实际上很简单 - 将第三个状态(例如值为 2)添加到此标志以表示“master 已完成”,并在结束时使用此状态(而不是初始状态 0) master 发出信号表示其工作已完成的路径。因此,只有一个调用 myFunc 的线程可以看到 0,这为您提供了所需的保证。要重用该标志,您需要将其显式重新初始化为 0。

关于c++ - C++中的比较和交换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27919854/

相关文章:

Python Multiprocessing,函数的一个参数是迭代器,Got TypeError

c++ - Visual studio 6.0(VC++引用问题)

c++ - 在数组中显示 1 和 0

c# - 使用固定指针将大型数据结构传递给非托管代码

java - 重新获取锁

python - Mlabwrap 多处理

C++:将成员指针初始化为null?

multithreading - 在单独的线程中更新 TWebBrowser?

c - 如何退出被信号量挂起的线程?

python - python 的 multiprocessing 和 concurrent.futures 有什么区别?