共享向量与原子布尔同步

Sel*_*_va 0 c++ multithreading mutex vector thread-safety

我有一个共享向量,可通过两个线程访问。

线程A的函数推入向量,线程B的函数将向量完全交换以进行处理。

    MovetoVec(PInfo* pInfo)
    {
       while(1)
        {
            if(GetSwitch())
            {
                swapBucket->push_back(pInfo);
                toggles = true;
                break;
            }
            else if(pInfo->tryMove == 5)
            {
                delete pInfo;
                break;
            }
            pInfo->tryMove++;
            Sleep(25);
        }
    }
Run Code Online (Sandbox Code Playgroud)

线程A试图使原子布尔值toggles变为true并推入向量。(上面的MoveToVec函数将被许多线程调用)。函数GetSwitch定义为

GetSwitch()
{
   if(toggles)
   {
      toggles = false;
      return TRUE;
   }
   else
        return FALSE;
}
Run Code Online (Sandbox Code Playgroud)

toggles 这是atomic_bool,线程B中另一个交换向量的函数是

    GetClassObj(vector<ProfiledInfo*>* toSwaps)
    {
        if(GetSwitch())
        {
            toSwaps->swap(*swapBucket);
            toggles = true;
        }
    }
Run Code Online (Sandbox Code Playgroud)

如果GetSwitch返回false,则threadB不执行任何操作。在这里我不使用任何锁定。它在大多数情况下都有效。但是有时pInfo,swapBucket 中的对象之一为NULL。我知道这是因为同步不良。

我遵循这种类型的GetSwitch()逻辑只是为了忽略由锁定引起的开销。我应该放弃这个并返回互斥或关键部分的东西吗?

Maa*_*lis 5

您的GetSwitch实现是错误的。多个线程有可能同时获取开关。

只有两个线程的这种情况的示例:

 Thread 1                 | Thread 2
--------------------------|--------------------------
 if (toggles)             |
                          | if (toggles)
     toggles = false;     |
                          |     toggles = false;
Run Code Online (Sandbox Code Playgroud)

if-test和赋值不是原子操作,因此不能单独用于同步线程。


如果要使用原子布尔作为同步手段,则需要在一个原子操作中比较并交换该值。幸运的是,C ++提供了一种称为的操作std::compare_exchange,该操作有很弱的作用(弱的操作可能会虚假地失败,但在循环中调用时会便宜一些)。

使用此操作,您的GetSwitch方法将变为:

bool GetSwitch()
{
    bool expected = true; // The value we expect 'toggles' to have
    bool desired = false; // The value we want 'toggles' to get

    // Check if 'toggles' is as expected, and if it is, update it to the desired value
    bool result = toggles.compare_exchange_strong(&expected, desired);

    // The result of the compare_exchange is true if the value was updated and false if it was not
    return result;
}
Run Code Online (Sandbox Code Playgroud)

这将确保自动比较和更新值。

请注意,C ++标准不保证原子布尔值是无锁的。在您的情况下,您也可以使用std::atomic_flag标准保证无锁的产品!仔细阅读该示例,它的工作原理与原子变量略有不同。


正如您正在尝试做的那样,编写无锁代码非常复杂且容易出错。

我的建议是先编写带锁的代码,并确保它是100%正确的。互斥体实际上出奇地快,因此在大多数情况下性能应该还可以。良好的锁定性能阅读:http//preshing.com/20111118/locks-arent-slow-lock-contention-is

仅在分析了代码并确信锁会影响性能之后,才应尝试编写无锁的代码。然后再分析一次,因为无锁代码不一定更快。