• 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++并发与多线程学习笔记--线程之间调度的更多相关文章

  1. C++并发与多线程学习笔记--基本概念和实现

    基本概念 并发 可执行程序.进程.线程 学习心得 并发的实现方法 多进程并发 多线程并发 总结 C++标准库 基本概念 (并发.进程.线程)区分C++初级编程和中高级编程 并发 两个或者更多的任务同时 ...

  2. C++并发与多线程学习笔记--单例设计模式、共享数据分析

    设计模式 共享数据分析 call_once 设计模式 开发程序中的一些特殊写法,这些写法和常规写法不一样,但是程序灵活,维护起来方便,别人接管起来,阅读代码的时候都会很痛苦.用设计模式理念写出来的代码 ...

  3. C++并发与多线程学习笔记--参数传递详解

    传递临时对象 陷阱 总结 临时对象作为线程参数 线程id的概念 临时对象构造时的抓捕 成员函数指针做线程函数 传递临时对象作为线程参数 创建的工作线程不止一个,线程根据编号来确定工作内容.每个线程都需 ...

  4. 【多线程】Android多线程学习笔记——线程池

    Java线程池采用了享元设计模式,在系统中维持一定数量的线程,用于处理异步或并发需求,在平时处理异步或并发任务时被广泛使用.这里基于JDK1.8和Android28来整理一些关于线程池的知识点. 一. ...

  5. C++并发与多线程学习笔记--future成员函数、shared_future、atomic

    std::future的其他成员函数 std::shared_future 原子操作.概念.基本用法 多线程主要是为了执行某个函数,本文的函数的例子,采用如下写法 int mythread() { c ...

  6. C++并发与多线程学习笔记--互斥量、用法、死锁概念

    互斥量(mutex)的基本概念 互斥量的用法 lock(), unlock() std::lock_guard类模板 死锁 死锁演示 死锁的一般解决方案 std::lock()函数模板 std::lo ...

  7. C++并发与多线程学习笔记--atomic

    std::atomic std::async std::atomic 一般atomic原子操作,针对++,--,+=,^=是支持的,其他结果可能不支持. 注意 std::atomic<int&g ...

  8. C++并发与多线程学习笔记--多线程数据共享问题

    创建和等待多个线程 数据和共享问题分析 只读的数据 有读有写 其他案例 共享数据的保护案例代码 创建和等待多个线程 服务端后台开发就需要多个线程执行不同的任务.不同的线程执行不同任务,并返回执行结果. ...

  9. C++并发与多线程学习笔记--async、future、packaged_task、promise

    async future packaged_task promise async std:async 是个函数,用来启动一个异步任务,启动起来一个异步任务之后,返回一个std::futre对象,启动一 ...

随机推荐

  1. macOS & timer & stop watch

    macOS & timer & stop watch https://matthewpalmer.net/blog/2018/09/28/top-free-countdown-time ...

  2. background & background-image & border-image

    background & background-image & border-image https://developer.mozilla.org/en-US/docs/Web/CS ...

  3. vue & $router & History API

    vue & $router gotoTemplateManage(e) { e.preventDefault(); this.$router.push({ path: `/operate-to ...

  4. Flutter: moor_flutter库,简化sqlite操作

    入门 video moor_flutter 示例代码仓库 install dependencies: ... moor_flutter: dev_dependencies: ... moor_gene ...

  5. PAUL ADAMS ARCHITECT:爱丁堡的房屋价值创历史新高

    近日,英国知名房产公司保罗·亚当斯公司根据一组调查报告表示,今年第三季度,爱丁堡的房价再创历史新高,并向大家分析了原因. 保罗·亚当斯公司(公司编号:07635831)是英国一家著名的房地产公司,总部 ...

  6. django中间件介绍

    在学习django中间件之前,先来认识一下django的生命周期,如下图所示: django生命周期:浏览器发送的请求会先经过wsgiref模块处理解析出request(请求数据)给到中间件,然后通过 ...

  7. 18_MySQL之HAVING字句的使用

    本节涉及的sql语句: -- HAVING -- 错误示例 SELECT deptno FROM t_emp WHERE AVG(sal)>=2000 GROUP BY deptno; 因为wh ...

  8. 微信支付 V3 的 Java 实现 Payment Spring Boot-1.0.7.RELEASE 发布

    Payment Spring Boot 是微信支付V3的Java实现,仅仅依赖Spring内置的一些类库.配置简单方便,可以让开发者快速为Spring Boot应用接入微信支付. 功能特性 实现微信支 ...

  9. pytorch(15)损失函数

    损失函数 1. 损失函数概念 损失函数:衡量模型输出与真实标签的差异 \[损失函数(Loss Function): Loss = f(\hat y,y) \] \[代价函数(Cost Function ...

  10. MySQL使用入门--初识数据库

    MySQL使用入门 数据库概述 数据库是存放数据的仓库.在应用的开发中总是离不开数据的查询.处理.存储,例如图书管理系统就需要操纵和存储大量的数据.没有数据库之前我们使用文件存储数据,但是文件存储有很 ...