2013-12-11 229 views
229

我有两个用例。std :: unique_lock <std::mutex>或std :: lock_guard <std::mutex>?

答:我想通过两个线程同步访问队列。

B.我想通过两个线程将访问同步到一个队列并使用一个条件变量,因为其中一个线程将等待内容被其他线程存储到队列中。

对于用例A,我看到使用std::lock_guard<>的代码示例。对于用例B,我使用std::unique_lock<>来查看代码示例。

两者之间有什么区别,哪一个应该在哪个用例中使用?

回答

222

区别在于你可以锁定和解锁std::unique_lockstd::lock_guard只会在施工时锁定一次,并在销毁时解锁。

因此,对于用例B,您肯定需要std::unique_lock作为条件变量。在情况A这取决于你是否需要重新锁定警卫。

std::unique_lock还有其他一些功能,例如:构建时不立即锁定互斥锁,而是构建RAII封装(请参见here)。

std::lock_guard也提供了一个方便的RAII包装,但不能安全地锁定多个互斥锁。当你需要在有限的范围内的包装,例如:一个成员函数可以使用:

class MyClass{ 
    std::mutex my_mutex; 
    void member_foo() { 
     std::lock_guard<mutex_type> lock(this->my_mutex);    
     /* 
     block of code which needs mutual exclusion (e.g. open the same 
     file in multiple threads). 
     */ 

     //mutex is automatically released when lock goes out of scope   
}; 

要澄清chmike一个问题,在默认情况下std::lock_guardstd::unique_lock是相同的。 因此,在上述情况下,您可以用std::unique_lock替换std::lock_guard。但是,std::unique_lock可能会有更多的开销。

+1

用指令std :: unique_lock lock(myMutex);互斥锁是否会被构造函数锁定? – chmike

+2

@chmike是的,它会的。补充说明。 – inf

+0

如果我在使用情况A,使用lock_gard而不是unique_lock会更有效吗? – chmike

31

使用lock_guard除非您需要能够在不破坏lock的情况下手动unlock互斥体。

特别是,condition_variable在呼叫wait时睡觉时会解锁其互斥锁。这就是为什么lock_guard在这里是不够的。

+0

将lock_guard传递给其中一个条件变量的等待方法会很好,因为无论出于何种原因,等待结束时总会重新获取互斥锁。但是该标准仅为unique_lock提供了一个接口。这可能被认为是标准的一个缺陷。 –

+2

@Chris在这种情况下你仍然会破坏封装。等待方法需要能够从'lock_guard'中提取互斥并解除锁定,从而暂时打破守护的类不变。即使这种情况对用户不可见,但我认为在这种情况下不允许使用'lock_guard'的合理原因。 – ComicSansMS

+0

如果是这样,它将是不可见的,无法检测。 gcc-4.8做到了。 wait(unique_lock &)调用_thread_cond_wait(&_M_cond,__lock.mutex() - > native_handle())(请参阅libstdC++ - v3/src/C++ 11/condition_variable.cc),它调用pthread_cond_wait()(请参阅libgcc/gthr -posix.h)。对lock_guard也可以做同样的事情(但不是因为它不符合condition_variable的标准)。 –

73

lock_guardunique_lock几乎是一回事; lock_guard是一个限制版本,界面有限。

A lock_guard始终保持从其施工到销毁的锁定。可以在不立即锁定的情况下创建unique_lock,可以在其存在的任何时候解锁,并且可以将锁的所有权从一个实例转移到另一个实例。

因此,您总是使用lock_guard,除非您需要unique_lock的功能。 A condition_variable需要unique_lock

+4

'condition_variable需要一个unique_lock.' - 只有在'wait()'方面是_but_,详细阐述在我的对inf的评论 –

-6

正如其他人所提到的,std :: unique_lock跟踪互斥锁的锁定状态,因此您可以推迟锁定,直到锁定构建完成,然后在锁定破坏之前解除锁定。 std :: lock_guard不允许这样做。

似乎没有理由为什么std :: condition_variable等待函数不应该带一个lock_guard以及一个unique_lock,因为每当等待结束时(无论出于何种原因)互斥体都会自动重新获取以便不会导致任何语义违规。但是根据标准,要使用带条件变量的std :: lock_guard,必须使用std :: condition_variable_any而不是std :: condition_variable。

编辑:删除“使用pthreads接口std :: condition_variable和std :: condition_variable_any应该是相同的”。对望一眼,GCC的实现:

  • 的std :: condition_variable ::等待(的std :: unique_lock &)只是调用pthread_cond_wait()的底层并行线程条件变量相对于由unique_lock举行(互斥等同样可以对lock_guard也是如此,但并不是因为标准没有规定)
  • std :: condition_variable_any可以与任何可锁定的对象一起工作,包括一个根本不是互斥锁的对象(因此它甚至可以工作具有进程间信号量)
7

有一些共同的东西在lock_guardunique_lock之间以及某些差异。

但是在提出的问题的上下文中,编译器不允许将lock_guard与条件变量结合使用,因为当线程调用等待条件变量时,该互斥锁会自动解锁并在其他线程/线程通知并且当前线程被调用(等待),锁被重新获取。

这种现象违反了lock_guard的原则。 lock_guard只能构造一次,只能破坏一次。

因此lock_guard不能与条件变量组合使用,但unique_lock可以(因为unique_lock可以被锁定和解锁多次)。

+4

'他编译器不允许与条件变量结合使用lock_guard'这是错误的。它肯定**允许并且在notify()方面与'lock_guard'完美配合。只有'wait()'int方需要'unique_lock',因为'wait()'必须在检查条件时释放锁。 –

相关问题