一、条件变量std::condition_variable、wait()、notify_one()。
条件变量在多线程中的应用场景:假设现在有两个线程
- 线程A:等待一个条件满足
- 线程B:专门往消息队列中扔消息(数据)
线程A等待一个条件,线程B满足这个条件之后触发先线程A,然后线程A从等待的这个位置继续运行。
std::condition_variable是一个和条件相关的类,说白了就是等待一个条件达成,这个类需要和互斥量来配合工作,用的时候我们需要生成这个类的对象。
具体工作流程:在线程A用wait来等一个东西,如果第二参数lambda表达式返回值是false,那么wait()将解锁互斥量,并堵塞本行,堵塞到线程调用notify_one()成员函数为止;如果wait()没有第二参数,那么就跟第二参数lambda函数返回false效果一样wait()解锁sbguard,并堵塞本行,堵塞到线程B调用notify_one()成员函数为止。 当wait()恢复干活时,会不断尝试重新获取互斥量锁,获取不到就会卡在wait()不断获取,直到拿到锁然后上锁。
//
// Created by yangjq on 22-7-7.
//
#include <iostream>
#include <thread>
#include <vector>
#include <list>
#include <mutex>
#include <condition_variable>
using namespace std;
class A{
public:
//把玩家命令收到一个队列的线程
void inMsgRecvQueue(){
for(int i = 0; i < 100000; ++i){
cout << "inMsgRecvQueue" << i << endl;
std::unique_lock<std::mutex> sbguard(my_mutex1);
//拿到了锁
msgRecvQueue.push_back(i);//数字i就是玩家收到的命令
cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;
my_cond.notify_one();//尝试把wait()线程唤醒,执行完这行,out里的wait就会被唤醒
}
}
//数据从消息队列中取出的线程
void outMsgRecvQueue(){
int command = 0;
while(true){
std::unique_lock<std::mutex> sbguard(my_mutex1);
//用wait来等一个东西
//如果第二参数lambda表达式返回值是false,那么wait()将解锁互斥量,并堵塞本行
//堵塞到其他某个线程调用notify_one()成员函数为止
//如果wait()没有第二参数,那么就跟第二参数lambda函数返回false效果一样
//wait()解锁sbguard,,并堵塞本行,堵塞到其他某个线程调用notify_one()成员函数为止
//当wait()恢复干活时,会不断尝试重新获取互斥量锁
my_cond.wait(sbguard, [this]{//一个lambda就是一个可调用对象
if(!msgRecvQueue.empty()){
return true;
}
return false;
});
//流程只要能走到这里来,互斥锁一定时锁着的
command = msgRecvQueue.front();//返回前面的元素
msgRecvQueue.pop_front();
cout << "outMsgRecvQueue()执行,取出一个元素!" << command << endl;
sbguard.unlock();
}
private:
//容器,专门用于存储玩家发送过来的命令
std::list<int> msgRecvQueue;
std::mutex my_mutex1;
std::condition_variable my_cond;
};
int main()
{
//用成员函数作为线程函数的方法来写线程
A myobja;
std::thread myOutMsgObj(&A::outMsgRecvQueue, &myobja);
std::thread myInMsgObj(&A::inMsgRecvQueue, &myobja);
myInMsgObj.join();
myOutMsgObj.join();
cout << "main end !" << endl;
return 0;
}
二、notify_all()
notify_one()只能通知一个线程,notify_all()可以通知多个线程。