std :: unique_lock <std :: mutex>或std :: lock_guard <std :: mutex>?

chm*_*ike 318 c++ multithreading mutual-exclusion c++11 stdmutex

我有两个用例.

答:我想将两个线程的访问同步到队列.

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

对于用例AI,请参阅代码示例std::lock_guard<>.对于用例BI,请参阅使用的代码示例std::unique_lock<>.

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

inf*_*inf 312

不同之处在于您可以锁定和解锁std::unique_lock.std::lock_guard将仅在施工时锁定一次并在销毁时解锁.

因此对于用例B,您肯定需要一个std::unique_lock条件变量.在情况A中,它取决于您是否需要重新锁定防护装置.

std::unique_lock还有其他功能允许它例如:构建时不立即锁定互斥锁,而是构建RAII包装器(见这里).

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           
};
Run Code Online (Sandbox Code Playgroud)

为了澄清chmike一个问题,在默认情况下std::lock_guardstd::unique_lock是相同的.所以在上面的例子中,你可以替换std::lock_guardstd::unique_lock.但是,std::unique_lock可能会有更多的开销.

请注意,这些天应该使用std::scoped_lock而不是std::lock_guard.

  • @chmike嗯,我认为这不是效率问题而是功能问题.如果`std :: lock_guard`足以满足您的情况A,那么您应该使用它.它不仅避免了不必要的开销,而且还向读者展示了你永远不会解锁这个警卫的意图. (9认同)
  • `因此,对于用例B,你肯定需要一个std :: unique_lock作为条件变量` - 是_but_仅在`cv.wait()的线程中,因为该方法原子地释放互斥锁.在更新共享变量然后调用`cv.notify_one()`的另一个线程中,一个简单的`lock_guard`就足以锁定互斥锁的范围...除非你做的更精细,我无法想象!例如http://en.cppreference.com/w/cpp/thread/condition_variable - 适合我:) (6认同)
  • @chmike:理论上是的.但是,Mutices并不是完全轻量级的构造,因此`unique_lock`的额外开销可能与实际锁定和解锁互斥锁的成本相比相形见绌(如果编译器没有优化开销,这是可能的). (5认同)
  • @chmike是的,它会的.添加了一些说明. (3认同)
  • 为什么它被称为“独特”?它到底有什么独特之处呢? (3认同)
  • 它与shared_lock形成对比 - https://en.cppreference.com/w/cpp/thread/shared_lock(唯一与共享所有权)。 (3认同)
  • 使用指令std :: unique_lock <std :: mutex> lock(myMutex); 互斥锁会被构造函数锁定吗? (2认同)

Seb*_*edl 108

lock_guard并且unique_lock几乎是一样的; lock_guard是受限制的版本,界面有限.

A lock_guard始终保持从其构造到破坏的锁定.A unique_lock可以在没有立即锁定的情况下创建,可以在其存在的任何点解锁,并且可以将锁的所有权从一个实例转移到另一个实例.

所以你总是使用lock_guard,除非你需要的能力unique_lock.一个condition_variable需要unique_lock.

  • `condition_variable需要一个unique_lock. - 是_but_只在`wait()`侧,正如我对inf的评论中所阐述的那样. (8认同)

Com*_*sMS 45

lock_guard除非您需要能够unlock在不损坏的情况下手动使用互斥锁,否则请使用lock.

特别是,condition_variable在打电话给睡觉时解锁其互斥锁wait.这就是为什么lock_guard这里还不够.

  • @Chris关键是`lock_guard`根本不允许检索底层互斥锁.这是一个故意的限制,允许更简单的推理使用`lock_guard`的代码而不是使用`unique_lock`的代码.实现所要求的唯一方法是故意破坏`lock_guard`类的封装并将其实现暴露给另一个类(在本例中为`condition_variable`).对于条件变量的用户不必记住两种锁类型之间的差异的可疑优势,这是一个艰难的代价. (4认同)
  • @Chris你在哪里知道`condition_variable_any.wait`可以和`lock_guard`一起使用?该标准要求提供的Lock类型满足[`BasicLockable`](http://en.cppreference.com/w/cpp/concept/BasicLockable)要求(§30.5.2),其中`lock_guard`没有.只有它的底层互斥锁,但由于我之前指出的原因,`lock_guard`的接口不提供对互斥锁的访问. (4认同)
  • @Chris在这种情况下你仍然会打破封装.wait方法需要能够从`lock_guard`中提取互斥锁并将其解锁,从而暂时打破守卫的类不变量.尽管这对用户来说是不可见的,但我认为这是在这种情况下不允许使用`lock_guard`的正当理由. (3认同)

San*_*eep 9

lock_guard和之间存在某些共同点unique_lock和某些差异.

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

这种现象违背了原则lock_guard.lock_guard只能构造一次并且只能被破坏一次.

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

  • `他编译器不允许将lock_guard与条件变量结合使用.这是错误的.它确实**允许并且与`notify()`侧的`lock_guard`完美配合.只有`wait()`int方需要`unique_lock`,因为`wait()`必须在检查条件时释放锁. (5认同)

小智 5

缺少的一个区别是: std::unique_lock可以移动但std::lock_guard不能移动。

注意:两者都不能复制。