C++ 标准::唯一锁定<;std::mutex>;或标准::锁紧保护装置<;std::mutex>;?
我有两个用例 我想同步两个线程对队列的访问 B.我想同步两个线程对队列的访问,并使用一个条件变量,因为其中一个线程将等待另一个线程将内容存储到队列中 对于用例A,我看到使用的代码示例。对于用例B,我看到使用的代码示例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或更高版本上,考虑使用一个稍微改
这两者之间有什么区别?在哪种情况下我应该使用哪一个?使用
锁定保护
,除非您需要能够手动解锁中间的互斥锁而不破坏锁定
特别是,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;
类产品