互斥锁:放行一个线程,阻塞N个线程
条件变量:放心n个线程,阻塞N个线程,主要使用场景:生产者-消费者模型
只能使用独占的互斥锁,并且还得配合unique_lock
// ①
void wait (unique_lock<mutex>& lck);
// ②
template <class Predicate>
void wait (unique_lock<mutex>& lck, Predicate pred);
unique_lock<mutex> locker;
wait(locker, [=](){
return taskQueue.size() != maxSize;
})
wait的行为有三步:
其他用法:
就是两类:不带回调的,需要条件判断,有回调的,判断写回调函数里
template
cv_status wait_for (unique_lock& lck,
const chrono::duration& rel_time);
template
bool wait_for(unique_lock& lck,
const chrono::duration& rel_time, Predicate pred);
template
cv_status wait_until (unique_lock& lck,
const chrono::time_point& abs_time);
template
bool wait_until (unique_lock& lck,
const chrono::time_point& abs_time, Predicate pred);
#include
#include
#include
#include
#include
#include
using namespace std;
class TaskQueue
{
public:
// 生产者存数据
void put(const int& task)
{
// 现在到我工作了,加锁,阻塞其他线程
unique_lock<mutex> locker(myMx);
/*
为什么是while,而不是if呢?
如果只判断一次,队列是满的,我解开了锁,阻塞的当前进程,处于等待唤醒的阶段,
但是不止我一个人是生产者,可能另一个生产者紧接着抢到了锁,也阻塞在这,
唤醒的时候很多生产者都试图重新加锁进行工作,只有我一个人再次抢到了锁,那么其他人又被阻塞了,
由于只判断一次,我拿到锁就直接工作了,
终于我完成了工作,其他被阻塞的线程抢到锁了,但是这时候,由于我的工作,队列又满了,
但是其他人没有重新判断队列的状态!
总结就是:
1. 某个线程的工作导致队列的状态变化了,每次工作前需要重新判断,只有抢到锁并且二次确定队列真的满足条件才能工作
2. 系统可能会虚假唤醒(系统和硬件问题),如果不循环判断就可能出错
*/
while(taskQueue.size() == maxSize) // 队列是满的,我生产了没地方存,不生产,解开锁,让别人工作
{
// 当不满足生产者的条件的时候,当前线程无法工作,就把这个所有权放出去,阻塞当前线程,让其他线程工作
// 当其他线程工作完了,满足我的工作条件了,会唤醒我,我再重新加锁,阻塞其他线程
notEmpty.wait(locker);
}
// 队列没有满,我可以生产
taskQueue.push(task);
cout << "生产者:" << this_thread::get_id() << "放入数据:" << task << endl;
// 满足其他线程的工作条件了,唤醒其他线程
// 我生产完了,我生产了一个产品,通知消费者来消费
notEmpty.notify_one();
}
// 消费者取数据
void take()
{
// 现在到我工作了,加锁,阻塞其他线程
unique_lock<mutex> locker(myMx);
while (taskQueue.empty()) // 队列是空的,我没法工作
{
// 没法工作就别占着了,把锁解开,让其他人工作,他们做好了会叫我
notEmpty.wait(locker);
}
// 队列不空,满足工作条件,我开始工作
int task = taskQueue.front();
taskQueue.pop();
cout << "消费者:" << this_thread::get_id() << "取走数据:" << task << endl;
// 我消费完了,我消耗了一个产品,通知生产者干活
notFull.notify_all();
}
private:
int maxSize = 100;
queue<int> taskQueue;
mutex myMx;
condition_variable notFull;
condition_variable notEmpty;
};
int main()
{
thread product[5];
thread consumer[5];
TaskQueue tq;
for (int i = 0; i < 5; ++i)
{
product[i] = thread(&TaskQueue::put, &tq, i + 100);
consumer[i] = thread(&TaskQueue::take, &tq);
}
for (int i = 0; i < 5; ++i)
{
product[i].join();
consumer[i].join();
}
return 0;
}
这个可以使用的锁很多,包括递归互斥锁,超时互斥锁,超时递归互斥锁,以及lock_guard
mutex myMx;
condition_variable_any notFull;
condition_variable_any notEmpty;
void put(const int &task)
{
// 现在到我工作了,加锁,阻塞其他线程
// lock_guard locker(myMx);
myMx.lock();
notFull.wait(myMx, [=]()
{
if (taskQueue.size() == maxSize)
cout << "wait" << endl;
return taskQueue.size() != maxSize; });
// 队列没有满,我可以生产
taskQueue.push(task);
cout << "生产者:" << this_thread::get_id() << "放入数据:" << task << endl;
// 满足其他线程的工作条件了,唤醒其他线程
// 我生产完了,我生产了一个产品,通知消费者来消费
notEmpty.notify_one();
myMx.unlock();
}
condition_variable
另一个不同的地方是,他不需要unique_lock来管理,所以要传锁本身,就算使用lock_guard也得传锁对象儿而不是lock_guard对象