zoukankan      html  css  js  c++  java
  • C++并发与多线程学习笔记--线程之间调度

    • condition_variable
    • wait()
    • notify_one
    • notify_all

    condition_variable

    条件变量的实际用途:

    比如有两个线程A和B,在线程A中等待一个条件满足,(消息队列中有要处理的消息),线程B专门往队列中丢数据。当B往线程中放入数据,同时B通知线程A,开始往下执行。在服务器的后台设计中,有一个线程,阻塞式地读取消息,并且将其解析,放入队列中,此时线程B还通知A,要从队列中去拿请求,并进行处理。

    a) socket技术使得服务器中的程序能够像打开文件一样来读取数据。

    b) 线程B读取数据,并将其放入到消息队列中。

    c) 线程B唤醒线程A,让线程A从队列中拿数据。

    d) 服务器处理请求完成并返回结果。

    通过条件变量类可以使得A等待B:

    复习原先的代码:(通过双重锁定,使得每次都判断是否为空,如果为空那么就取得锁)

    class ProcessRequest {
    public:
    	//把命令加入到一个队列
    	void inMsgRecvQueue() {
    		
    		for (int i = 0; i < 100000; ++i) {
    			//std::lock_guard<std::mutex> sbguard(my_mutex);
    			cout << "插入一个元素" << endl;
    			m_msgRecvQueue.push_back(i); //假设这个队列表示玩家的命令
    
    		} //占用时间片
    	}
    
    	bool outMsgLULProc(int &command) {
    		//通过双重锁定,避免每次进来程序都锁定。
    		if (!m_msgRecvQueue.empty())
    		{
    			std::lock_guard<std::mutex> sbguard(my_mutex);
    
    			if (!m_msgRecvQueue.empty()) {
    				int command = m_msgRecvQueue.front();
    				m_msgRecvQueue.pop_front();
    				return true;
    			}
    			return false;
    		}
    	}
    	//把命令移出一个队列
    		void outMsgRecvQueue() {
    			int command = 0;
    			for (int i = 0; i < 100000; ++i) {
    				
    				bool result = outMsgLULProc(command);
    				if (result == true) {
    					cout << "outMsgRecvQueue() 执行,取出一个元素" << endl;
    				}
    				else
    				{
    					cout << "outMsgRecvQueue() 还执行,但是消息队列为空" << endl;
    					//消息队列为空
    				}
    				
    				//占用时间片
    			}
    		}
    	
    private:
    	std::list<int> m_msgRecvQueue; //容器,用于表示玩家的发送过来命令
    	std::mutex my_mutex;
    };
    

      使用类std::condition_variable来替代双重锁定,用来等待一个条件达成,这个类需要和互斥量配合工作,用的时候需要生成类的对象。

    pirvate:
        std::condition_variable my_condition;
    

      

    wait()

    出队列修改: wait是卡在这里的,需要修改入队列的线程。

    	void outMsgRecvQueue() {
    		int command = 0;
    		while (true) {
    			std::unique_lock<std::mutex> sbguard1(my_mutex);
    			my_condition.wait(sbguard1, [this] {
    				if (!m_msgRecvQueue.empty())//lambda表达式就是一个可调用对象(函数)
    					return true;
    				else
    					return false;
    			}); //wait用来等待一个东西 
    		//Wait(para1, para2) 
    		//para1: 互斥量
    		//para2:第二个参数Lambda表达式的返回值是False
    		//      那么将解锁互斥量,并阻塞本行,直到其他线程调用 notify_one()
    		//如果没有第二个参数,那么就跟第二个参数返回False效果一样
    		}
    	}
    

      当然wait()之后可以提早解开 unique_lock(),然后执行逻辑。

    notify_one

    将原来阻塞的进程唤醒了。wait就开始恢复干活了,恢复之后

    a) wait() 不断尝试获取互斥量锁,尝试拿这个锁。如果获取不到锁,流程就卡在wait这里,如果获取到,wait就走下来了。

    b) 实际上获取到了锁就等于上了锁。如果wait有第二个参数(lambda),就判断lambda表达式,

         如果表达式为false,又将互斥量解锁。然后另一个线程又休眠。

         如果表达式为true,则wait返回,流程走下来(此时互斥锁被锁着)。

         如果wait没有第二个参数,则wait返回

    	void inMsgRecvQueue() {
    		
    		for (int i = 0; i < 100; ++i) {
    			std::lock_guard<std::mutex> sbguard(my_mutex);
    			m_msgRecvQueue.push_back(i); //假设这个队列表示玩家的命令
    			cout << "插入一个元素" << endl;
    			my_condition.notify_one();
    		} //占用时间片
    	}
    

     

    同时获取锁的可能性:

    1) void inMsgRecvQueue()

    2) void outMsgRecvQueue()

    可能出现同时竞争一个锁的可能性,也就是说如果运行到了outMsgRecvQueue()的逻辑执行语句的时候,队列中至少进去了一个元素,那么就有可能出现in和out并不是按序执行的情况。

     out在执行逻辑语句的时候有延迟,此时如果in唤醒,out并不是卡在wait()的状态,那么此时notify_one()调用就没有效果。

    深入思考

    写代码用在商业中,必须理解。

    在线程入口函数中, 队列中可能会存在多条数据,这个时候处理不过来怎么办?开更多的线程处理?或者限流,超过200条数据未处理,就卡住?

    notify_all

  • 相关阅读:
    C#学习-多态
    C#学习-子类的初始化顺序
    C#学习-面向对象
    Python数据类型知识点全解
    python 复制图片到剪贴板
    pyperclip
    pyautogui
    多线程代码案例
    常用正则表达式最强整理(速查手册)
    python os
  • 原文地址:https://www.cnblogs.com/rynerlute/p/11839268.html
Copyright © 2011-2022 走看看