C++11 生产者消费者模型
线程互斥 lock_guard
使用lock_guard管理互斥锁。在退出作用域后进行析构时就会自动解锁,从而保证了互斥量的正确操作,避免忘记 unlock() 操作而导致线程死锁
std::lock_guard<std::mutex> lck(mtx);
- 线程lock_guard生命周期内自动加锁和解锁,其中加锁和解锁分别在构造函数和析构函数中完成
- 不能对lock_guard进行拷贝或赋值。lock_guard不能用于参数传递或返回值中。
线程互斥 unique_lock
unique_lock有着 lock_guard一样的功能,比 lock_guard更灵活,可以手动上锁与解锁。但是比 lock_guard更占用资源。
- unique_lock提供了右值引用参数的拷贝构造与赋值,可以用于用于参数传递或返回值中。
- unique_lock可以在合适的位置调用lock()与unlock(),不像 lock_guard只能自动上锁与解锁
- unique_lock可以取代lock_guard
std::unique_lock<std::mutex> lck(mtx);
线程同步 条件变量 condition_variable
与互斥锁(unique_lock)一起使用,实现线程同步
std::condition_variable cv;
cv.wait(lck); //使线程进入等待状态 并且释放互斥锁
cv.notify_all();//通知其他线程 其他线程得到该通知,就会从等待状态变为阻塞状态,等着拿到锁
生产者消费者代码示例
#include<iostream>
#include<thread>
#include<mutex>
#include<condition_variable>
#include<queue>
using namespace std;
std::mutex mtx;
std::condition_variable cv;
//C++的stl容器都不是线程安全的 需要自己封装出线程安全的类
class Queue {
public:
void put(int val) {
//将互斥锁用unique_lock封装 更安全
//如果此线程没拿到锁 就会阻塞在这 如果拿到了锁 unique_lock就会在出作用域时自动释放锁
unique_lock<std::mutex> lck(mtx);
//que中还有商品 就先让消费者消费 消费完了 再继续生产
while (!que.empty()) {
cv.wait(lck); //使生产者进入等待状态 并且释放互斥锁
}
que.push(val);
//通知消费者可以消费了,消费者得到该通知,就会从等待状态变为阻塞状态,拿到锁后就会执行消费
cv.notify_all();
cout << "生产者生产物品:" << val << endl;
}
int get() {
unique_lock<std::mutex> lck(mtx); //将互斥锁用unique_lock封装 更安全
//que中没有商品 就先让生产者生产
while (que.empty()) {
cv.wait(lck); //使消费者进入等待状态 并且释放互斥锁
}
int val = que.front();
que.pop();
cv.notify_all();//消费了 通知生产者生产
cout << "消费者消费物品:" << val << endl;
return val;
}
private:
queue<int>que;
};
//生产者线程
void producer(Queue *que) {
for (int i = 1; i <= 10; i++) {
que->put(i);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
}
//消费者线程
void consumer(Queue *que) {
for (int i = 1; i <= 10; i++) {
que->get();
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
}
int main() {
Queue que;
std::thread t1(producer,&que);
std::thread t2(consumer,&que);
t1.join();
t2.join();
return 0;
}