使用条件变量的最经典的场景就是生产者和消费者
1.最普通的生产者和消费者代码
#include
#include
#include
#include
#include
using namespace std;
std::deque Q;
std::mutex mu;
void producers() {
int count = 10;
while (count > 0) {
std::unique_lock locker(mu,std::defer_lock);
locker.lock();
Q.push_front(count);
locker.unlock();
std::this_thread::sleep_for(chrono::seconds(1));
count--;
}
}
void consumers() {
int data = 0;
while (data!=1)
{
std::unique_lock locker(mu);
if (!Q.empty()) {
data = Q.back();
Q.pop_back();
locker.unlock();
std::cout << "consumer get data :" << data << endl;
}
else {
locker.unlock();
}
}
}
int main(int argc, char** argv) {
std::thread t1(producers);
std::thread t2(consumers);
t1.join();
t2.join();
return 0;
}
这里的代码有个问题就是consumers()中while循环中,如果Q为空的时候,就会不停的执行else{ locker.unlock();}。这是相当资源的。那么我们对这段代码进行
改进下。
2.改进1,让consumer等待
consumers在进行一次Q判断为空的情况,让线程休息一下,这里我们修改一下consumers().
void consumers() {
int data = 0;
while (data!=1)
{
std::unique_lock locker(mu);
if (!Q.empty()) {
data = Q.back();
Q.pop_back();
locker.unlock();
std::cout << "consumer get data :" << data << endl;
}
else {
locker.unlock();
std::this_thread::sleep_for(chrono::microseconds(5));
}
}
}
这里我们添加这行代码
std::this_thread::sleep_for(chrono::microseconds(5));
让线程休息5秒
但是我们仔细想想,这样合理吗,可能有更好的解决方案,比如生产者生产出来一个产品,再通知消费者,不就可以解决消费者循环等待的问题了嘛。
4.条件变量
生产者生产出来一个产品,再通知消费者,不就可以解决消费者循环等待的问题。利用这个思想代码就可以写成这样
#include
condition_varible cond;
这里是引入条件变量的头文件和创建一个全局的条件变量
void producers() {
int count = 10;
while (count > 0) {
std::unique_lock locker(mu,std::defer_lock);
locker.lock();
Q.push_front(count);
locker.unlock();
cond.notify_one();
std::this_thread::sleep_for(chrono::seconds(1));
count--;
}
}
这里在生产者生产了一个产品后,使用cond.notify_one(),来激活一个消费者。如果有多个消费者可以notify_all()
void consumers() {
int data = 0;
while (data != 1)
{
std::unique_lock locker(mu);
cond.wait(locker);
data = Q.back();
Q.pop_back();
locker.unlock();
std::cout << "consumer get data :" << data << endl;
}
}
消费者要一直wait(),直到生产者告诉它产品生产好了。
当然这里代码也是有问题的,会不会出现伪激活的情况呢,道理简单就是Q不为空时使用,就是前提。可以写成一个lamda函数。
所以我们还要对cond.wait(locker)进行修改
//void wait( std::unique_lock& lock, Predicate pred );
cond.wait(locker, []() {return !Q.empty(); });