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++标准库 基本概念 (并发.进程.线程)区分C++初级编程和中高级编程 并发 两个或者更多的任务同时 ...
- C++并发与多线程学习笔记--单例设计模式、共享数据分析
设计模式 共享数据分析 call_once 设计模式 开发程序中的一些特殊写法,这些写法和常规写法不一样,但是程序灵活,维护起来方便,别人接管起来,阅读代码的时候都会很痛苦.用设计模式理念写出来的代码 ...
- C++并发与多线程学习笔记--参数传递详解
传递临时对象 陷阱 总结 临时对象作为线程参数 线程id的概念 临时对象构造时的抓捕 成员函数指针做线程函数 传递临时对象作为线程参数 创建的工作线程不止一个,线程根据编号来确定工作内容.每个线程都需 ...
- 【多线程】Android多线程学习笔记——线程池
Java线程池采用了享元设计模式,在系统中维持一定数量的线程,用于处理异步或并发需求,在平时处理异步或并发任务时被广泛使用.这里基于JDK1.8和Android28来整理一些关于线程池的知识点. 一. ...
- C++并发与多线程学习笔记--future成员函数、shared_future、atomic
std::future的其他成员函数 std::shared_future 原子操作.概念.基本用法 多线程主要是为了执行某个函数,本文的函数的例子,采用如下写法 int mythread() { c ...
- C++并发与多线程学习笔记--互斥量、用法、死锁概念
互斥量(mutex)的基本概念 互斥量的用法 lock(), unlock() std::lock_guard类模板 死锁 死锁演示 死锁的一般解决方案 std::lock()函数模板 std::lo ...
- C++并发与多线程学习笔记--atomic
std::atomic std::async std::atomic 一般atomic原子操作,针对++,--,+=,^=是支持的,其他结果可能不支持. 注意 std::atomic<int&g ...
- C++并发与多线程学习笔记--多线程数据共享问题
创建和等待多个线程 数据和共享问题分析 只读的数据 有读有写 其他案例 共享数据的保护案例代码 创建和等待多个线程 服务端后台开发就需要多个线程执行不同的任务.不同的线程执行不同任务,并返回执行结果. ...
- C++并发与多线程学习笔记--async、future、packaged_task、promise
async future packaged_task promise async std:async 是个函数,用来启动一个异步任务,启动起来一个异步任务之后,返回一个std::futre对象,启动一 ...
随机推荐
- React Native Apps
React Native Apps https://github.com/ReactNativeNews/React-Native-Apps github app https://github.com ...
- Regular Expressions all in one
Regular Expressions all in one Regular Expressions Cheatsheet https://developer.mozilla.org/en-US/do ...
- DOM事件对象用法
分为三个阶段:事件捕获阶段.目标阶段.事件冒泡阶段. 事件捕获老版本浏览器(IE<=8)不支持,但是事件冒泡可以放心使用. 事件处理程序 一共四类写法,基本都见过,看下写法就知道怎么回事儿了. ...
- 新手如何通过SPC算力生态获得多重收益?
DeFi市场在去年的一波又一波热潮之后,在今年余温有些褪去.而资本市场也将目光从DeFi市场中转移开来,他们将目光对准了新的市场,即算力市场.算力,其实从区块链技术在大范围普及以来,就是一个常见的话题 ...
- IntelliJ IDEA 还能画思维导图,果然最强 IDE!
最近栈长发现 IntelliJ IDEA 居然还能画思维导图,太牛逼了! 当然这得借助 IDEA 的 UML 插件,因为它本身也是一个 UML 图,所以这篇就从 UML 图开撕,看 IDEA 怎么画思 ...
- Java基础语法:final修饰符
一.final类 描述: 用'final'修饰的类不能被继承,没有子类. 例如,我们是无法写一个类去继承String类,然后对String类型扩展的,因为API中已经将String类定义为'final ...
- DRF的orm多表关系补充及serializer子序列化
目录 一.控制多表关系的字段属性 1.如何建立基表 2.断开连表关系 3.四种级联关系 二.子序列化 一.控制多表关系的字段属性 1.如何建立基表 要在基表中配置Meta,设置abstract=Tru ...
- 记录vue springboot 跨域采坑
vue配置 域名src\main.js要与config\index.js一样 var axios = require('axios')axios.defaults.baseURL = 'http:// ...
- 如果要是把标记为2的那一行Lable1.Text改为其他的Lable显示执行代码
转: 如果要是把标记为2的那一行Lable1.Text改为其他的Lable显示执行代码 如图,程序很简单,文件路径也没问题,为什么会报错,百思不得其解?[url]https://book.douban ...
- HDOJ-1711(KMP算法)
Number Sequence HDOJ-1711 1.这里使用的算法是KMP算法,pi数组就是前缀数组. 2.代码中使用到了一个技巧就是用c数组看成是复合字符串,里面加一个特殊整数位-1000006 ...