admin管理员组

文章数量:1599543

 《1》条件变量std::condition_variable和其成员函数.wait()、.notify_one()

        std::condition_variable:实际上是一个类,是一个和条件相关的类,说白了就是等待一个条件达成。(注意:只有unique_lockcondition_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