所以没有技术原因吗?
我支持cmeerw的回答,因为我相信他给出了技术原因。让我们来看一看。让我们假装委员会决定condition_variable
等一等mutex
。这是使用该设计的代码:
void foo()
{
mut.lock();
while (not_ready)
cv.wait(mut);
mut.unlock();
}
这正是不应该使用的方式condition_variable
。在标有:
有一个例外的安全问题,这是一个严重的问题。如果在这些区域(或cv.wait
单独)引发了异常,则互斥体的锁定状态将泄漏,除非在其他地方也放置了try / catch来捕获并解锁该异常。但这只是您要程序员编写的更多代码。
假设程序员知道如何编写异常安全代码,并且知道如何使用unique_lock
它来实现。现在,代码如下所示:
void foo()
{
unique_lock<mutex> lk(mut);
while (not_ready)
cv.wait(*lk.mutex());
}
这好多了,但仍然不是一个好情况。该condition_variable
界面使程序员不愿花力气使工作正常进行。如果lk
偶然不引用互斥锁,则可能存在空指针取消引用。而且没有办法condition_variable::wait
检查该线程是否拥有该锁mut
。
哦,只是记住,程序员也可能选择错误的unique_lock
成员函数来公开互斥量。 *lk.release()
在这里将是灾难性的。
现在,让我们看一下如何使用带有以下代码的实际condition_variable
API编写代码unique_lock<mutex>
:
void foo()
{
unique_lock<mutex> lk(mut);
while (not_ready)
cv.wait(lk);
}
- 这段代码很简单。
- 这是异常安全的。
- 该
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
。
更新资料
Bamboon在下面的评论中建议与我进行对比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>
数据成员。
由于此新功能比基本功能贵condition_variable::wait
,并且condition_variable
是这样的低级工具,因此将这个非常有用但更昂贵的功能放在单独的类中,以便仅在使用时才付费。