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 } 
  1. 这个代码非常简单。
  2. 这是exception安全的。
  3. 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_lockcondition_variable::wait的自然select。

更新

在下面的评论中,bamboonbuild议我对比condition_variable_any ,所以这里是:

问题:为什么不是condition_variable::wait模板化,以便可以将任何可Lockabletypes传递给它?

回答:

这是非常酷的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 ++语言的重点已经转向了默认的安全(但是如果用户想要并且努力的话,仍然可以让用户投入自己的脚步)。