参考资料
c++ 11 多线线程系列-----------生产者消费者
C11线程管理:互斥锁
需求
需要构成一个生产者消费者模式,生产者和消费者是无继承关系的两个类的各自的对象,作用与两个不同的线程中。产品库设计在两个线程之外。
方法
将产品库容器的地址作为参数传入线程函数,在线程中将该地址传入类的构造函数,在对象中对该地址进行互斥的读写。
关键原料
#include <mutex>
std::mutex g_lock;
提供一个锁,其实也考虑过CRITICAL_SECTION
,其开销较mutex
更低,但是下面的方法
#include <condition_variable>
std::condition_variable not_empty;
需要配合mutex
使用,具体方法为
std::unique_lock<std::mutex> lck(g_lock);
not_empty.wait(lck); // 挂起当前线程
...
not_empty.notify_all(); // 释放
由于g_lock
和not_empty
是全局变量,可以在另一线程中为被挂起的当前线程进行释放。
还有一点,unique_lock<mutex>
的使用可以省去lock()
和unlock()
语句的繁琐,提升了安全性又不失自由度。(默认在作用域生命周期结束时解锁)。
实例
代码
#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
#include <condition_variable>
#include <queue>
std::mutex g_lock; // 互斥
std::condition_variable not_empty; // 挂起条件
class CSHIT {
int shittime;
std::queue<char>* shitpool;
public:
CSHIT(std::queue<char> * sp) {
shittime = 3;
shitpool = sp;
}
void makeshit() {
std::unique_lock<std::mutex> lck(g_lock);
std::cout << "Making shit:" << std::this_thread::get_id() << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(shittime));
(*shitpool).push('S');
not_empty.notify_all(); // 一旦生产一枚产品,通知消费者线程释放
std::cout << "Shit made:" << std::this_thread::get_id() << std::endl;
showshitpoolsize();
}
private:
void showshitpoolsize() {
std::cout << "Shit Pool Size: " << (*shitpool).size() << std::endl;
}
};
class CEAT {
int eattime;
std::queue<char>* shitpool;
public:
CEAT(std::queue<char>* sp) {
eattime = 1;
shitpool = sp;
}
void eatshit() {
std::unique_lock<std::mutex> kcl(g_lock);
while ((*shitpool).empty()) {
// 检查产品区是否有产品
std::cout << "I'm hungry!!" << std::endl;
not_empty.wait(kcl); // 若无产品,挂起当前线程
}
std::cout << "Eating shit:" << std::this_thread::get_id() << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(eattime));
(*shitpool).pop();
std::cout << "Shit eaten:" << std::this_thread::get_id() << std::endl;
showshitpoolsize();
}
private:
void showshitpoolsize() {
std::cout << "Shit Pool Size: " << (*shitpool).size() << std::endl;
}
};
void vFunc1(std::queue<char>* sp)
{
CSHIT shit(sp);
for (int s = 0; s < 10; ++s) {
shit.makeshit();
}
}
void vFunc2(std::queue<char>* sp) {
CEAT eat(sp);
for (int j = 0; j < 10; ++j) {
eat.eatshit();
}
}
int main()
{
std::queue<char> ShitPool;
for (int i = 0; i < 3; ++i) {
ShitPool.push('S');
}
std::thread t1(vFunc1, &ShitPool);
std::thread t2(vFunc2, &ShitPool);
t1.join();
t2.join();
return 0;
}
结果
Eating shit:4776
Shit eaten:4776
Shit Pool Size: 2
Eating shit:4776
Shit eaten:4776
Shit Pool Size: 1
Making shit:16284
Shit made:16284
Shit Pool Size: 2
Making shit:16284
Shit made:16284
Shit Pool Size: 3
Making shit:16284
Shit made:16284
Shit Pool Size: 4
Eating shit:4776
Shit eaten:4776
Shit Pool Size: 3
Eating shit:4776
Shit eaten:4776
Shit Pool Size: 2
Eating shit:4776
Shit eaten:4776
Shit Pool Size: 1
Eating shit:4776
Shit eaten:4776
Shit Pool Size: 0
I'm hungry!!
Making shit:16284
Shit made:16284
Shit Pool Size: 1
Making shit:16284
Shit made:16284
Shit Pool Size: 2
Making shit:16284
Shit made:16284
Shit Pool Size: 3
Making shit:16284
Shit made:16284
Shit Pool Size: 4
Making shit:16284
Shit made:16284
Shit Pool Size: 5
Making shit:16284
Shit made:16284
Shit Pool Size: 6
Making shit:16284
Shit made:16284
Shit Pool Size: 7
Eating shit:4776
Shit eaten:4776
Shit Pool Size: 6
Eating shit:4776
Shit eaten:4776
Shit Pool Size: 5
Eating shit:4776
Shit eaten:4776
Shit Pool Size: 4
Eating shit:4776
Shit eaten:4776
Shit Pool Size: 3
I'm hungry!!
提示消费者线程被挂起。线程之间的运转由CPU的scheduler
分配,可视为随机。