所以没有技术原因?
我赞成 cmeerw 的回答,因为我相信他给出了技术原因。让我们来看看它。让我们假设委员会决定让condition_variable 等待mutex。这是使用该设计的代码:
void foo()
{
mut.lock();
// mut locked by this thread here
while (not_ready)
cv.wait(mut);
// mut locked by this thread here
mut.unlock();
}
这正是一个不应该使用condition_variable的方式。在标有:
的地区
// mut locked by this thread here
存在异常安全问题,而且很严重。如果在这些区域(或cv.wait 本身)引发异常,则互斥锁的锁定状态会泄漏,除非在某个地方也放置了 try/catch 来捕获异常并解锁它。但这只是您要求程序员编写的更多代码。
假设程序员知道如何编写异常安全代码,并且知道使用unique_lock 来实现它。现在代码如下所示:
void foo()
{
unique_lock<mutex> lk(mut);
// mut locked by this thread here
while (not_ready)
cv.wait(*lk.mutex());
// mut locked by this thread here
}
这好多了,但仍然不是一个很好的情况。 condition_variable 接口让程序员不遗余力地让事情顺利进行。如果lk 意外未引用互斥体,则可能存在空指针取消引用。并且condition_variable::wait 无法检查该线程是否拥有mut 上的锁。
哦,刚刚记住了,还有程序员可能选择错误的unique_lock成员函数来暴露互斥锁的危险。 *lk.release() 在这里会是灾难性的。
现在让我们看看代码是如何使用带有unique_lock<mutex> 的实际condition_variable API 编写的:
void foo()
{
unique_lock<mutex> lk(mut);
// mut locked by this thread here
while (not_ready)
cv.wait(lk);
// mut locked by this thread here
}
- 此代码非常简单。
- 这是异常安全的。
-
wait 函数可以检查lk.owns_lock(),如果是false,则抛出异常。
这些是推动condition_variable API 设计的技术原因。
此外,condition_variable::wait 不采用lock_guard<mutex>,因为lock_guard<mutex> 就是你所说的:我拥有这个互斥锁的锁,直到lock_guard<mutex> 销毁。但是当您调用condition_variable::wait 时,您会隐式释放互斥锁上的锁。因此该操作与lock_guard 用例/语句不一致。
无论如何,我们都需要unique_lock,以便可以从函数返回锁,将它们放入容器中,并以异常安全的方式锁定/解锁非作用域模式中的互斥锁,因此unique_lock 是@987654351 的自然选择@。
更新
bamboon 在下面的 cmets 中建议我对比 condition_variable_any,所以这里是:
问题:为什么不对condition_variable::wait 进行模板化以便我可以将任何Lockable 类型传递给它?
答案:
这是一个非常酷的功能。例如,this paper 演示了在条件变量上以共享模式等待shared_lock (rwlock) 的代码(这在 posix 世界中闻所未闻,但非常有用)。但是功能更昂贵。
因此委员会推出了具有此功能的新类型:
`condition_variable_any`
有了这个condition_variable 适配器,人们可以等待任何可锁定的类型。如果它有成员lock() 和unlock(),你就可以走了。 condition_variable_any 的正确实现需要 condition_variable 数据成员和 shared_ptr<mutex> 数据成员。
因为这个新功能比你的基本condition_variable::wait 更昂贵,而且因为condition_variable 是一个如此低级的工具,所以这个非常有用但更昂贵的功能被放在一个单独的类中,这样你只需在以下情况下付费你用它。