C++ 标准::唯一锁定<;std::mutex>;或标准::锁紧保护装置<;std::mutex>;?

C++ 标准::唯一锁定<;std::mutex>;或标准::锁紧保护装置<;std::mutex>;?,c++,multithreading,c++11,mutual-exclusion,stdmutex,C++,Multithreading,C++11,Mutual Exclusion,Stdmutex,我有两个用例 我想同步两个线程对队列的访问 B.我想同步两个线程对队列的访问,并使用一个条件变量,因为其中一个线程将等待另一个线程将内容存储到队列中 对于用例A,我看到使用的代码示例。对于用例B,我看到使用的代码示例 这两者之间有什么区别?在哪种情况下我应该使用哪一个?使用锁定保护,除非您需要能够手动解锁中间的互斥锁而不破坏锁定 特别是,condition\u变量在调用wait进入睡眠状态时解锁其互斥锁。这就是为什么锁紧装置在这里是不够的 如果你已经在C++ 17或更高版本上,考虑使用一个稍微改

我有两个用例

我想同步两个线程对队列的访问

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

对于用例A,我看到使用的代码示例。对于用例B,我看到使用的代码示例


这两者之间有什么区别?在哪种情况下我应该使用哪一个?

使用
锁定保护
,除非您需要能够手动
解锁中间的互斥锁而不破坏
锁定

特别是,
condition\u变量
在调用
wait
进入睡眠状态时解锁其互斥锁。这就是为什么
锁紧装置
在这里是不够的


如果你已经在C++ 17或更高版本上,考虑使用一个稍微改进的版本LoopyGueGue/Cuff>,具有相同的基本功能。

< P>区别在于你可以锁定和解锁一个<代码> STD::UnQuyYOLLUX >代码>标准::锁紧装置
在施工时仅锁定一次,在销毁时解锁

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

std::unique_lock
具有其他功能,例如:在不立即锁定互斥锁的情况下构建互斥锁,但构建RAII包装(请参阅)

std::lock\u 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
    }           
};
class-MyClass{
std::mutex my_mutex;
无效成员_foo(){
std::lock\u guard lock(这个->我的互斥锁);
/*
需要互斥的代码块(例如,打开相同的
文件(在多个线程中)。
*/
//当锁超出范围时,互斥锁将自动释放
}           
};
要通过chmike澄清问题,默认情况下
std::lock\u guard
std::unique\u lock
是相同的。 因此,在上述情况下,您可以将
std::lock\u guard
替换为
std::unique\u lock
。但是,
std::unique_lock
的开销可能会稍大一些


请注意,现在(从C++17开始)人们应该使用而不是
std::lock\u-guard
lock\u-guard
unique\u-lock
几乎是一样的东西
lock_guard
是一个具有有限接口的受限版本

一个
锁紧装置
从构造到破坏始终保持一把锁。可以创建一个
唯一的\u锁
,而无需立即锁定,可以在其存在的任何点解锁,并且可以将锁的所有权从一个实例转移到另一个实例


所以您总是使用
lock\u-guard
,除非您需要
unique\u-lock
的功能。
condition\u变量
需要一个
unique\u lock

正如其他人所提到的,std::unique\u lock跟踪互斥锁的锁定状态,因此您可以将锁定延迟到构建锁之后,并在销毁锁之前解锁。std::锁紧装置不允许这样做

似乎没有理由认为std::condition_变量wait函数不应该采用锁保护和唯一锁,因为每当等待结束时(无论出于何种原因),互斥锁都会自动重新获取,这样就不会导致任何语义冲突。但是,根据标准,要将std::lock\u保护与条件变量一起使用,必须使用std::condition\u变量\u any而不是std::condition\u变量

编辑:删除了“使用pthreads接口std::condition_变量和std::condition_变量应相同”。关于gcc的实施情况:

  • std::condition_variable::wait(std::unique_lock&)只调用底层pthread条件变量上的pthread_cond_wait()
  • std::condition_variable_any可以处理任何可锁定对象,包括一个根本不是互斥锁的对象(因此它甚至可以处理进程间信号量)

锁紧装置
独特锁紧装置
之间有一些共同点,也有一些区别

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

这种现象违反了锁紧装置的原理<代码>锁紧装置
只能构造一次,只能销毁一次


因此,
lock\u-guard
不能与条件变量结合使用,但是
unique\u-lock
可以(因为
unique\u-lock
可以多次锁定和解锁)。

它们实际上不是相同的互斥体,
lock\u-guard
std::mutex
几乎相同,不同的是,它的生命周期结束于作用域的末尾(称为D-tor),因此需要对这两个互斥体进行明确定义:

具有在作用域块期间拥有互斥锁的机制

是一个包装器,允许延迟锁定、有时间限制的锁定尝试、递归锁定、锁所有权转移以及与条件变量一起使用

以下是一个示例实现:

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <chrono>

using namespace std::chrono;

class Product{

   public:

       Product(int data):mdata(data){
       }

       virtual~Product(){
       }

       bool isReady(){
       return flag;
       }

       void showData(){

        std::cout<<mdata<<std::endl;
       }

       void read(){

         std::this_thread::sleep_for(milliseconds(2000));

         std::lock_guard<std::mutex> guard(mmutex);

         flag = true;

         std::cout<<"Data is ready"<<std::endl;

         cvar.notify_one();

       }

       void task(){

       std::unique_lock<std::mutex> lock(mmutex);

       cvar.wait(lock, [&, this]() mutable throw() -> bool{ return this->isReady(); });

       mdata+=1;

       }

   protected:

    std::condition_variable cvar;
    std::mutex mmutex;
    int mdata;
    bool flag = false;

};

int main(){

     int a = 0;
     Product product(a);

     std::thread reading(product.read, &product);
     std::thread setting(product.task, &product);

     reading.join();
     setting.join();


     product.showData();
    return 0;
}
#包括
#包括
#包括
#包括
#包括
#包括
使用名称空间std::chrono;
类产品