那么没有技术原因吗?
我赞成 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();
}
这正是一个不应该 use a 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
}
- 这段代码非常简单。
- 它是异常安全的。
- The
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
.
Update
竹子在下面的评论中建议我对比condition_variable_any
,所以这里是:
问题:为什么不是condition_variable::wait
模板化,以便我可以通过任何Lockable
键入它?
Answer:
这是一个非常酷的功能。例如这张纸 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3427.html演示等待 a 的代码shared_lock
(rwlock) 以共享模式对条件变量进行操作(这在 posix 世界中是闻所未闻的,但仍然非常有用)。然而,该功能的成本更高。
因此委员会引入了一种具有此功能的新类型:
`condition_variable_any`
有了这个condition_variable
adaptor可以等待any可锁定类型。如果有会员的话lock()
and unlock()
, 你已准备好出发。正确实施condition_variable_any
需要一个condition_variable
数据成员和一个shared_ptr<mutex>
数据成员。
因为这个新功能比你的基本功能更昂贵condition_variable::wait
,并且因为condition_variable
是一个如此低级的工具,这个非常有用但更昂贵的功能被放入一个单独的类中,这样您只需在使用它时付费。