admin管理员组文章数量:1599543
《1》条件变量std::condition_variable和其成员函数.wait()、.notify_one()
std::condition_variable:实际上是一个类,是一个和条件相关的类,说白了就是等待一个条件达成。(注意:只有unique_lock和condition_variable配合使用才能使用其成员函数!)
.wait():wait()用来等一个东西。
1、如果wait()的第2个参数的返回值是true,那么wait()直接返回,并继续执行后续的代码
2、如果wait()的第2个参数的返回值是false,那么wait()将解锁互斥量,并堵塞到本行
(堵塞到啥时候为止呢?堵塞到其他某个线程调用.notify_one()这个condition_variable类的成员函数为止)
3、如果wait()没有第2个参数 ==> my_condition.wait(sbguard);此时就和第2个参数的返回值为false的效果是一样的!wait()将解锁互斥量,并堵塞到本行。堵塞到其他某个线程调用到
.notify_one()这个condition_variable类的成员函数为止。
.notify_one():一次只能通知一个的线程,唤醒这一个线程中的condition_variable类的.wait()函数!也即(2个线程中)唤醒另一个线程中的wait()函数。
当这个wait恢复后,会做2件事情:
1、唤醒后的wait()会不断尝试获取互斥量锁,如果获取不到那么流程就卡在wait()这里继续等待获取,如果获取到了,那么wait()就继续往下执行代码(获取到了锁就表示此时又给该线程上锁了)
2.1、被唤醒后,如果wait有第二个参数就判断第2个参数(谓词)。
a)如果此时谓词返回值为false,那wait又对互斥量解锁,然后又休眠,等待再次被notify_one()唤醒,再重复尝试拿到互斥锁并判断第2个参数,如此循环反复下去。
b)如果此时谓词返回值为true,则wait返回,流程可以继续往下执行了(此时互斥量还是已被锁住)。
2.2、如果wait没有第二个参数,则wait返回,流程走下去(此时互斥量还是已被锁住)。
注意:流程只要走到了wait()的下面则互斥量一定是被锁住了的。
demo_codes:
#include<iostream>
#include<thread>
#include<list>
#include<mutex>
#include<condition_variable>
using namespace std;
class A {
private:
list<int> msgRecvQueue;//处理命令消息的容器
mutex my_mutex;//创建一个互斥量!
//mutex my_mutex2;//创建一个互斥量!
std::condition_variable my_condition;//创建一个条件变量类 的 对象
public:
//把收到的消息(玩家命令)插入到消息容器中的线程函数
void inMsgRecvQueue() {
for (int i = 0; i < 10000; i++) {
cout << "inMsgRecvQueue()执行,插入一个元素: " << i << endl;
std::unique_lock<std::mutex> sbguard1(my_mutex);
msgRecvQueue.push_back(i);//假设这个数字i就是我收到的命令
my_condition.notify_one();
//这行代码执行完毕后, outMsgRecvQueue()中的wait()函数就会给唤醒!
}
return;
}
//把数据从消息容器中取出的线程函数
void outMsgRecvQueue() {
int command = 0;
while (true) {
//wait()函数是用来等 待一个东西的
//如果第2个参数(谓词)返回值为false时,那么wait()将解锁互斥量mutex,并堵塞到本行
//那堵塞到啥时候为止呢?堵塞到其他某个线程调用std::condition_variable类对象的.notify_one()成员函数为止;
/*
wait()用来等一个东西
如果wait()的第2个参数的返回值是true,那么wait()直接返回,并继续执行后续的代码
如果wait()的第2个参数的返回值是false,那么wait()将解锁互斥量,并堵塞到本行
堵塞到啥时候为止呢?堵塞到其他某个线程调用.notify_one()这个condition_variable类的成员函数为止
如果wait()没有第2个参数 ==> my_condition.wait(sbguard);
此时就和第2个参数的返回值为false的效果是一样的!
wait()将解锁互斥量,并堵塞到本行。堵塞到其他某个线程调用.notify_one()这个condition_variable类的成员函数为止
*/
std::unique_lock<std::mutex> sbguard(my_mutex);
my_condition.wait(sbguard, [this](void) {
if (!this->msgRecvQueue.empty())return true;
return false;
});
command = msgRecvQueue.front();//返回第一个元素,但是不检查元素是否存在
msgRecvQueue.pop_front();//移除消息容器中的首元素
//my_condition.notify_one
sbguard.unlock();//这是因为unique_lock的灵活性,so我们可以随时地unlock解锁,以免锁住太长时间!
cout << "outMsgRecvQueue()执行,取出一个元素 " << command << endl;
}
}
};
int main(void) {
A mytobj;
thread Inthread(&A::inMsgRecvQueue, &mytobj);//&mytobj也可以写为std::ref(mytobj)
//也只有这样才能够传真正的引用进去到线程函数中!
thread Outthread(&A::outMsgRecvQueue, &mytobj);
Inthread.join();
Outthread.join();
cout << "主线程执行完毕,进程准备退出!" << endl;
return 0;
}
《2》对上述代码之深入思考
上面的代码可能导致出现一种情况:
因为outMsgRecvQueue()与inMsgRecvQueue()并不是一对一执行的,所以当程序循环执行很多次以后,在msgRecvQueue 中可能已经有了很多命令消息了。但是,outMsgRecvQueue还是被唤醒一次只处理一条数据,这时命令消息多到你的代码处理不过来了。可以考虑把outMsgRecvQueue多执行几次(用循环),或者对inMsgRecvQueue进行限流。以达到优化这个多线程代码的目的。
要想写好这种代码,必须要多写多练!
《3》.notify_all():
.notify_all():一次能通知all(2个及以上)的线程中的condition_variable类的.wait()函数,并唤醒他们的.wait()函数!(唤醒后wait就会判断其第2个参数是否返回true,是的话就给当前这个线程上锁并继续往下执行,直到unlock;若为false,则继续释放锁,等待下一次唤醒了再重新判断!如此循环反复下去...)
补充:
wjw老师说,这一小节的知识点,如果觉得自己用不好的话,可以选择不用,换而使用别的更加稳定的普通写法也ok,因为写多线程的代码最重要的一个要求就是你的代码必须保持稳定!
本文标签: conditionvariablewaitnotifyallnotifyone
版权声明:本文标题:Th8:condition_variable、wait、notify_one、notify_all(重要) 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/dianzi/1728322155a1153966.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论