C/C++条件变量使用说明
C/C++条件变量使用说明
一、使用方法
C语言中,条件变量主要配合互斥锁,用于实现“生产者-消费者”模型,使用方法如下:
生产者:
- 获取互斥锁 pthread_mutex_lock
- 生产商品
- 通知消费者 pthread_cond_signal
- 释放互斥锁 pthread_mutex_unlock
消费者:
- 获取互斥锁 pthread_mutex_lock
- 判断是否存在可消费商品,是则执行3,否则等待生产者通知,直到存在商品 pthread_cond_wait
- 消费商品
- 释放互斥锁 pthread_mutex_unlock
二、原理分析
1、条件变量存在的意义
在“生产者-消费者”模型中,有两大需求:
- 保护临界资源“商品”。
- “商品”生产完成后,及时通知消费者。
针对需求1,可以使用互斥锁,保证临界资源的安全;针对需求2,初始的想法是,是否可以复用需求1中的互斥锁呢,因为当消费者去获取锁时,如果该锁正被生产者占用,那么消费者线程将被挂起,随者生产者完成本次商品的生产释放锁,消费者线程就立刻被唤醒,似乎是可以满足需求的,流程变为如下所示:
生产者:
- 获取互斥锁 pthread_mutex_lock
- 生产商品
通知消费者 pthread_cond_signal- 释放互斥锁 pthread_mutex_unlock
消费者:
- 获取互斥锁 pthread_mutex_lock
判断是否存在可消费商品,是则执行3,否则等待生产者通知,直到存在商品 pthread_cond_wait- 消费商品
- 释放互斥锁 pthread_mutex_unlock
通过简单分析,就可以得出,该方式存在巨大问题:在生产者未进行生产时,消费者每次都将无任何阻碍的获得锁,发现没有商品,又立刻释放锁,从而导致线程空转,极大占用CPU处理能力。
因此,需要引入一种机制,当消费者发现无商品可处理时,将自身挂起,当有新的商品产生时,立刻唤醒处理,由此,条件变量便产生了。
2、条件变量的本质
等待条件变量(pthread_cond_wait)可依次拆分为三个操作:释放互斥锁、等待在条件变量上、再次获取互斥锁。由此,可以理解消费者检查商品并等待的逻辑为:
while(没有商品?)
pthread_cond_wait(mutex);
即判断如果没有商品,则释放互斥锁,等待在条件变量上;当生产者生产了商品后,消费者立刻被唤醒,又再次获取互斥锁,重复以上的逻辑。
三、举例说明
为了使代码更简练,使用c++进行举例,说明如何使用条件变量,实现“生产者-消费者”模型:
#include <iostream>
#include <thread>
#include <condition_variable>
#include <string>
#include <mutex>
#include <atomic>
#include <unistd.h>
using namespace std;
mutex m;
condition_variable cv;
atomic<bool> stop(false);
unsigned int products = 0;
void consumer_thead()
{
while(!stop || products) {
unique_lock<mutex> lk(m);
cv.wait(lk, []{return products;});
cout << "consumer thread is consuming product, remain "
<< products << "\n";
products--;
}
}
void producer_thread()
{
int total = 0;
while(!stop) {
m.lock();
products += 2;
total += 2;
cout << "producer thread produced product, remain "
<< products << " total " << total << endl;
if (10 <= total) {
cout << "will stop produce" << endl;
stop = true;
}
cv.notify_one();
m.unlock();
sleep(1);
}
}
int main()
{
thread consumer(consumer_thead);
thread producer(producer_thread);
consumer.join();
producer.join();
return 0;
}