共享向量与原子布尔同步

时间:2017-07-03 09:38:58

标签: c++ multithreading vector thread-safety mutex

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

线程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);
        }
    }

线程A尝试将原子布尔值toggles设置为true并推送到向量中。(上面的MoveToVec函数将被许多线程调用)。函数GetSwitch定义为

GetSwitch()
{
   if(toggles)
   {
      toggles = false;
      return TRUE;
   }
   else
        return FALSE;
}

toggles这里是atomic_bool.Bnd交换向量的另一个函数是

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

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

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

1 个答案:

答案 0 :(得分:5)

您的GetSwitch实施错误。多个线程可以同时获取交换机。

仅有两个主题的此类场景示例:

 Thread 1                 | Thread 2
--------------------------|--------------------------
 if (toggles)             |
                          | if (toggles)
     toggles = false;     |
                          |     toggles = false;

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;
}

这将确保比较和更新值以原子方式发生。

请注意,C ++标准不保证原子布尔值无锁。在您的情况下,您也可以使用std::atomic_flag,保证按标准无锁!仔细阅读这个例子,它与原子变量有点不同。

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

我的建议是先用锁写代码并确保它是100%正确的。互斥体实际上速度惊人,因此在大多数情况下性能应该没问题。关于锁定性能的一个很好的解读:http://preshing.com/20111118/locks-arent-slow-lock-contention-is

只有在您对代码进行了分析并确信自己锁定正在影响性能之后,您才应该尝试编写无锁代码。然后再次进行配置,因为无锁代码不一定更快。