C ++ 11:为什么std :: condition_variable使用std :: unique_lock?
当我使用std::unique_lock
时,我对std::unique_lock
的作用感到困惑。 据我了解的文件 , std::unique_lock
基本上是一个臃肿的锁守卫,可以交换两个锁之间的状态。
我到目前为止已经使用了pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
来达到这个目的(我猜这就是STL在posix上使用的)。 它需要一个互斥锁,而不是一个锁。
这里有什么区别? std::condition_variable
处理std::unique_lock
是一个优化的事实吗? 如果是这样,它究竟如何更快?
所以没有技术上的原因?
我赞成cmeerw的回答,因为我相信他给了技术上的理由。 我们来看看吧。 假设委员会决定让condition_variable
等待一个mutex
。 这是使用该devise的代码:
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
本身)引发exception,则互斥锁的locking状态将被泄漏,除非try / catch也放在某处以捕获exception并将其解锁。 但是,这只是程序员写的更多的代码。
假设程序员知道如何编写exception安全的代码,并知道使用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
的locking。
哦,只记得,还有程序员可能select错误的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 }
- 这个代码非常简单。
- 这是exception安全的。
-
wait
函数可以检查lk.owns_lock()
,如果它是false
,则抛出exception。
这些是推动condition_variable
的APIdevise的技术原因。
此外, condition_variable::wait
不会带上lock_guard<mutex>
因为lock_guard<mutex>
就是这样说的:我拥有对这个mutex的锁,直到lock_guard<mutex>
破坏为止。 但是当你调用condition_variable::wait
,你隐式释放互斥锁。 所以这个操作与lock_guard
用例/语句不一致。
我们无论如何需要unique_lock
,以便可以从函数返回锁,将它们放到容器中,并以exception安全的方式locking/解锁非范围模式中的互斥锁,所以unique_lock
是condition_variable::wait
的自然select。
更新
在下面的评论中,bamboonbuild议我对比condition_variable_any
,所以这里是:
问题:为什么不是condition_variable::wait
模板化,以便可以将任何可Lockable
types传递给它?
回答:
这是非常酷的function。 例如, 本文演示了在共享模式下等待shared_lock
(rwlock)的条件variables(在posix世界中闻所未闻,但非常有用)的代码。 但是function更为昂贵。
所以委员会介绍了一种新的function:
`condition_variable_any`
有了这个condition_variable
适配器可以等待任何可locking的types。 如果它有成员lock()
和unlock()
,你很好走。 condition_variable_any
的正确实现需要一个condition_variable
数据成员和一个shared_ptr<mutex>
数据成员。
因为这个新的function比基本的condition_variable::wait
更加昂贵,而且因为condition_variable
是一个低级的工具,所以这个非常有用但是更昂贵的function被放到一个单独的类中,所以如果你使用它,你只需要付钱。
这本质上是一个APIdevise决策,使得API在默认情况下尽可能安全(额外的开销被忽略)。 通过要求传递一个unique_lock
而不是一个原始的mutex
,API的用户被定向写入正确的代码(在有例外的情况下)。
近年来,C ++语言的重点已经转向了默认的安全(但是如果用户想要并且努力的话,仍然可以让用户投入自己的脚步)。