我有一个由两个线程访问的共享 vector 。
线程 A 的一个函数压入 vector ,线程 B 的一个函数完全交换 vector 以进行处理。
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 尝试将原子 bool 值 切换
为 true 并插入 vector。(上面的 MoveToVec
函数将被许多线程调用)。函数 GetSwitch 定义为
GetSwitch()
{
if(toggles)
{
toggles = false;
return TRUE;
}
else
return FALSE;
}
toggles
这里是 atomic_bool。另一个来自线程 B 的交换 vector 的函数是
GetClassObj(vector<ProfiledInfo*>* toSwaps)
{
if(GetSwitch())
{
toSwaps->swap(*swapBucket);
toggles = true;
}
}
如果 GetSwitch 返回 false,threadB 什么都不做。在这里我不使用任何锁定。它适用于大多数情况。但有时 swapBucket 中的 pInfo
对象之一为 NULL。我知道这是因为同步不良。
我遵循这种类型的 GetSwitch() 逻辑只是为了忽略锁定引起的开销。我应该放弃这个并回到互斥锁或关键部分的东西吗?
最佳答案
您的 GetSwitch
实现有误。多个线程可以同时获取开关。
只有两个线程的这种情况的示例:
Thread 1 | Thread 2
--------------------------|--------------------------
if (toggles) |
| if (toggles)
toggles = false; |
| toggles = false;
if-test 和赋值不是原子操作,因此不能单独用于同步线程。
如果要使用原子 bool 值作为同步手段,则需要在一个原子操作中比较和交换值。幸运的是,C++ 提供了这样一个名为 std::compare_exchange
的操作。 ,它有 weak 和 strong 两种风格(弱的可能会虚假地失败,但在循环中调用时更便宜)。
使用此操作,您的 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++ 标准不保证原子 bool 值是无锁的。在你的情况下,你也可以使用 std::atomic_flag
标准保证是无锁的!不过请仔细阅读示例,它的工作方式与原子变量略有不同。
正如您尝试做的那样,编写无锁代码非常复杂且容易出错。
我的建议是先写带锁的代码,并确保它 100% 正确。互斥量实际上出奇地快,因此在大多数情况下性能应该没问题。关于锁性能的好读物:http://preshing.com/20111118/locks-arent-slow-lock-contention-is
只有在分析了代码并确信锁会影响性能后,您才应该尝试编写无锁代码。然后再次分析,因为无锁代码不一定更快。
关于c++ - 共享 vector 与原子 bool 同步,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44882289/