C++并发与多线程学习笔记

Wesley13
• 阅读 288
  • condition_variable
  • wait()
  • notify_one
  • notify_all

condition_variable

条件变量的实际用途:

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

C++并发与多线程学习笔记

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

点赞
收藏
评论区
推荐文章
秃头王路飞 秃头王路飞
4个月前
webpack5手撸vue2脚手架
webpack5手撸vue相信工作个12年的小伙伴们在面试的时候多多少少怕被问到关于webpack方面的知识,本菜鸟最近闲来无事,就尝试了手撸了下vue2的脚手架,第一次发帖实在是没有经验,望海涵。languageJavaScript"name":"vuecliversion2","version":"1.0.0","desc
技术小男生 技术小男生
4个月前
linux环境jdk环境变量配置
1:编辑系统配置文件vi/etc/profile2:按字母键i进入编辑模式,在最底部添加内容:JAVAHOME/opt/jdk1.8.0152CLASSPATH.:$JAVAHOME/lib/dt.jar:$JAVAHOME/lib/tools.jarPATH$JAVAHOME/bin:$PATH3:生效配置
Jacquelyn38 Jacquelyn38
1年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
blmius blmius
1年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Stella981 Stella981
1年前
Opencv中Mat矩阵相乘——点乘、dot、mul运算详解
Opencv中Mat矩阵相乘——点乘、dot、mul运算详解2016年09月02日00:00:36 \牧野(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fme.csdn.net%2Fdcrmg) 阅读数:59593
Stella981 Stella981
1年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
1年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Stella981 Stella981
1年前
Github标星5300+,专门为程序员开发文档开源管理系统,我粉了
!(https://oscimg.oschina.net/oscnet/a11909a041dac65b1a36b2ae8b9bcc5c432.jpg)码农那点事儿关注我们,一起学习进步!(https://oscimg.oschina.net/oscnet/f4cce1b7389cb00baaab228e455da78d0
Stella981 Stella981
1年前
Nginx反向代理upstream模块介绍
!(https://oscimg.oschina.net/oscnet/1e67c46e359a4d6c8f36b590a372961f.gif)!(https://oscimg.oschina.net/oscnet/819eda5e7de54c23b54b04cfc00d3206.jpg)1.Nginx反
helloworld_34035044 helloworld_34035044
6个月前
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为