所以没有技术原因吗?
我赞成 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()
在这里将是灾难性的。
现在让我们看看代码是如何使用实际condition_variable
API 编写的unique_lock<mutex>
:
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
。
这些都是推动 API 设计的技术原因condition_variable
。
此外,condition_variable::wait
不需要,lock_guard<mutex>
因为lock_guard<mutex>
你说的是:我拥有这个互斥锁上的锁,直到lock_guard<mutex>
销毁。但是当您调用 时condition_variable::wait
,您会隐式释放互斥锁上的锁。因此该操作与lock_guard
用例/语句不一致。
我们unique_lock
无论如何都需要,以便可以从函数返回锁,将它们放入容器中,并以一种异常安全的方式锁定/解锁非作用域模式中的互斥锁,因此unique_lock
自然而然地选择了condition_variable::wait
.
更新
竹子在下面的评论中建议我对比condition_variable_any
,所以这里是:
问题: 为什么没有condition_variable::wait
模板化以便我可以将任何Lockable
类型传递给它?
回答:
这是一个非常酷的功能。例如,本文演示了shared_lock
在条件变量上以共享模式等待 (rwlock) 的代码(这在 posix 世界中闻所未闻,但非常有用)。然而,功能更昂贵。
因此委员会推出了具有此功能的新类型:
`condition_variable_any`
使用此condition_variable
适配器,您可以等待任何可锁定的类型。如果它有成员lock()
和unlock()
,你就可以走了。的正确实现condition_variable_any
需要一个condition_variable
数据成员和一个shared_ptr<mutex>
数据成员。
因为这个新功能比你的 basic 更昂贵condition_variable::wait
,而且因为condition_variable
它是一个低级工具,所以这个非常有用但更昂贵的功能被放在一个单独的类中,这样你只有在使用它时才需要付费。