我有两个用例。
答:我想通过两个线程同步对队列的访问。
B.我想同步两个线程对队列的访问并使用条件变量,因为一个线程将等待内容被另一个线程存储到队列中。
对于用例AI,请参见使用的代码示例std::lock_guard<>
。有关用例BI,请参见使用的代码示例std::unique_lock<>
。
两者之间有什么区别?在哪种用例中应该使用哪一种?
我有两个用例。
答:我想通过两个线程同步对队列的访问。
B.我想同步两个线程对队列的访问并使用条件变量,因为一个线程将等待内容被另一个线程存储到队列中。
对于用例AI,请参见使用的代码示例std::lock_guard<>
。有关用例BI,请参见使用的代码示例std::unique_lock<>
。
两者之间有什么区别?在哪种用例中应该使用哪一种?
Answers:
区别在于您可以锁定和解锁std::unique_lock
。std::lock_guard
只会在施工时锁定一次,在破坏时解锁。
因此,对于用例B,您肯定需要std::unique_lock
条件变量a。如果是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
};
通过chmike来澄清问题,默认情况下std::lock_guard
与std::unique_lock
相同。因此,在上述情况下,您可以替换std::lock_guard
为std::unique_lock
。但是,std::unique_lock
可能会有更多的开销。
请注意,这几天应该使用std::scoped_lock
而不是std::lock_guard
。
std::lock_guard
足以满足您的情况A,则应使用它。它不仅避免了不必要的开销,而且还向读者表明了您永远不会解锁此防护装置的意图。
unique_lock
,实际锁定和解锁互斥锁的成本(如果编译器没有优化该开销,则可能)会使mutex 的额外开销相形见)。
So for usecase B you definitely need a std::unique_lock for the condition variable
-是的,但仅在cv.wait()
s 线程中,因为该方法自动释放互斥体。在另一个线程中,您更新共享变量然后调用cv.notify_one()
,一个简单lock_guard
的锁就足以锁定互斥锁...除非您做了我无法想象的更复杂的事情!例如en.cppreference.com/w/cpp/thread/condition_variable-对我有用 :)
lock_guard
并且unique_lock
是几乎同样的事情; lock_guard
是具有受限接口的受限版本。
一个lock_guard
始终持有其建设,其破坏的锁。unique_lock
可以在不立即锁定的情况下创建A ,可以在存在的任何时刻解锁A ,并且可以将锁的所有权从一个实例转移到另一个实例。
因此lock_guard
,除非您需要的功能,否则请始终使用unique_lock
。A condition_variable
需要一个unique_lock
。
A condition_variable needs a unique_lock.
-是的,但仅在wait()
ing方面,如我对inf的评论所述。
使用lock_guard
除非您需要能够手动unlock
之间的互斥锁而不破坏lock
。
特别是,condition_variable
在呼叫时进入睡眠状态时,解锁其互斥锁wait
。因此,lock_guard
此处a 不够。
lock_guard
并将其解锁,从而临时破坏防护的类不变性。即使这种情况对用户来说是不可见的,我仍认为这是lock_guard
在这种情况下不允许使用的正当理由。
lock_guard
根本不允许检索基础互斥量。这是一个有意的限制,允许lock_guard
相对于使用的代码,对使用的代码进行更简单的推理unique_lock
。实现您所要求的唯一方法是故意破坏lock_guard
该类的封装,并将其实现公开给其他类(在本例中为condition_variable
)。对于条件变量的用户不必记住两种锁类型之间的区别而付出的可疑优势,这是一个艰难的代价。
condition_variable_any.wait
可以与a一起使用的想法lock_guard
?该标准要求提供的Lock类型满足BasicLockable
要求(第30.5.2节),lock_guard
但不满足。仅它的底层互斥锁可以使用,但是出于我之前指出的原因,该接口lock_guard
不提供对互斥锁的访问。
之间存在某些共同点lock_guard
,unique_lock
并且存在某些差异。
但是在所问问题的上下文中,编译器不允许lock_guard
与条件变量结合使用,因为当线程调用等待条件变量时,互斥锁会自动解锁,并且当其他线程通知当前线程时调用(退出等待状态)后,将重新获取该锁。
这种现象违反了原理lock_guard
。lock_guard
只能被构造一次,而只能被破坏一次。
因此lock_guard
不能与条件变量结合使用,而是unique_lock
可以使用a(因为unique_lock
可以多次锁定和解锁)。
he compiler does not allow using a lock_guard in combination with a condition variable
这是错误的。这当然不会允许,并用完美的作品lock_guard
在notify()
荷兰国际集团的一面。只有wait()
int端需要unique_lock
,因为wait()
在检查条件时必须释放锁。
它们实际上不是相同的互斥锁,lock_guard<muType>
与几乎相同std::mutex
,不同之处在于其生命周期在作用域的末尾(称为D-tor)结束,因此对这两个互斥锁有清晰的定义:
lock_guard<muType>
具有在作用域块持续时间内拥有互斥量的机制。
和
unique_lock<muType>
是一个包装器,允许延迟锁定,锁定时限尝试,递归锁定,锁所有权转移以及与条件变量一起使用。
这是一个示例实现:
#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;
}
在此示例中,我使用了unique_lock<muType>
withcondition variable
正如其他人提到的那样,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的实现时: