基本概念
c++11中的条件变量 std::condition_variable是线程同步原语
条件变量是解决哪些线程同步问题?试用场景是什么?
顾名思义,所谓条件变量可以理解为一个条件,线程间进行同步通信的时候,通过这个修改和检查这个条件的状态,来进行某些操作。
例如:生产者生产数据到队列,消费者从队列拿数据,其中生产者生产数据到队列的条件是条件1:队列不能满;而消费者从队列拿数据的条件是条件2:队列中有数据。生产者受条件1制约,同时能够改变条件2状态;消费者受条件2制约,同时可以改变条件1状态。所以生产者和消费者可以通过这两个条件进行线程同步。
流程与原理
等待条件线程:
- 加互斥锁
- 检查条件是否满足,如果不满足则需要等待条件cv.wait,等他条件满足和其他线程的唤醒;
- 条件满足并被唤醒后继续下步操作
修改条件线程:
- 加互斥锁
- 修改条件
- cv.notyfy()
上述流程中,对于两个线程而言,这两个条件变量共享的,都可以进行访问,所以需要使用锁保证数据的一致性,如果涉及到共享的临界资源也放入到锁的作用域。
wait函数:
函数会释放锁lock,判断条件是否满足,如果不满足则进入阻塞状态,等待其他线程唤醒(notify);被其他线程唤醒后,检查条件是否满足,若满足则再次获取锁。
while (!stop_waiting()) {
wait(lock);
}
生产者消费者示例
// 使用条件变量实现多生产消费关系
// 编译方式:g++ producerConsumer.cc -std=c++11 -lpthread -o producerConsumer
// 运行方式 ./producerConsumer arg1 arg2 如:./producerConsumer 1 1
// 其中 arg1 表示生产周期; arg2 表示消费周期;可以调节这两个参数控制生产速度和消费速度
#include <istream>
#include <mutex>
#include <thread>
#include <queue>
#include <condition_variable>
#include <iostream>
#include <atomic>
using namespace std;
// 模拟源数据数据
std::atomic_int data;
// 生产者消费者通讯队列, 队列长度限制 10
std::queue<int> dataQueue;
const int MAX_QUEUE_SIZE = 10;
// 条件变量
std::condition_variable cvNotFull;
std::condition_variable cvNotEmpty;
std::mutex mtx;
bool stop = false;
// 生产者消费者的睡眠时间,可以通过修改睡眠时间控制其生产消费速度
int P_SLEEP_TIME = 1;
int C_SLEEP_TIME = 2;
const int CV_WAIT_TIME = 3;
// 生产者线程,id表示编号,
void producer(int id)
{
while (!stop)
{
{
// 作用域内锁有效
std::unique_lock<std::mutex> lk(mtx);
cvNotFull.wait_for(lk, std::chrono::seconds(CV_WAIT_TIME), []
{ return dataQueue.size() < MAX_QUEUE_SIZE; });
if (dataQueue.size() < MAX_QUEUE_SIZE)
{
cout << "producer " << id << ": push data " << data << endl;
dataQueue.push(data++);
}
}
cvNotEmpty.notify_one();
std::this_thread::sleep_for(std::chrono::seconds(P_SLEEP_TIME));
}
cout << "producer " << id << "quit " << endl;
}
// 消费者线程,id表示编号
void consumer(int id)
{
while (!stop)
{
{
// 作用域内锁有效
std::unique_lock<std::mutex> lk(mtx);
cvNotEmpty.wait_for(lk, std::chrono::seconds(CV_WAIT_TIME), []
{ return !dataQueue.empty(); });
if (!dataQueue.empty())
{
int datas = dataQueue.front();
dataQueue.pop();
cout << "consumer " << id << ": get data " << datas << endl;
}
}
cvNotFull.notify_one();
std::this_thread::sleep_for(std::chrono::seconds(C_SLEEP_TIME));
}
cout << "consumer " << id << "quit " << endl;
}
int main(int argc, char **argv)
{
if (argc == 3)
{
P_SLEEP_TIME = stoi(string(argv[1]));
C_SLEEP_TIME = stoi(string(argv[2]));
cout << "producer sleep time: " << P_SLEEP_TIME << endl;
cout << "consumer sleep time: " << C_SLEEP_TIME << endl;
}
data = 0;
// 模拟2个生产者,2个消费者
std::thread p1(producer, 1);
std::thread p2(producer, 2);
std::thread c1(consumer, 1);
std::thread c2(consumer, 2);
p1.detach();
p2.detach();
c1.detach();
c2.detach();
// 运行30s
std::this_thread::sleep_for(std::chrono::seconds(30));
stop = true;
// 等待生产者和消费者线程退出
std::this_thread::sleep_for(std::chrono::seconds(10));
return 0;
}
运行结果: