• 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. Dart Web

    Dart Web Dart for Web https://dart.dev/platforms dart2js xgqfrms 2012-2020 www.cnblogs.com 发布文章使用:只允 ...

  2. Asp.Net Core学习笔记:(二)视图、模型、持久化、文件、错误处理、日志

    TagHelper 入门 优点:根据参数自动生成,不需要手写超链接,类似Django模板里面的url命令. 在ViewImport中添加TagHelper @addTagHelper *,Micros ...

  3. canal数据同步的环境配置

    canal数据同步的环境配置:(适用于mysql) 前提:在linux和windows系统的mysql数据库中创建相同结构的数据库和表,我的linux中mysql是用docker实现的(5.7版本), ...

  4. TERSUS无代码开发(笔记09)-简单实例前端样式设计

    前端常用样式设计 =========================================================================================== ...

  5. Linux安装与使用

    1.安装 1.1安装VMware 1.1.1VM12版本安装 1)下载:网盘:链接:https://pan.baidu.com/s/1Jnr--KIy3bSTvRhtB8nfiQ 提取码:czna 2 ...

  6. Codeforces Global Round 13

    比赛地址 A(水题) 题目链接 题目: 给出一个\(01\)序列,有2种操作:1.将某个位置取反:2.询问\(01\)序列中第\(k\)大的数 解析: 显然维护1的数目即可 #include<b ...

  7. vue导出数据excel

    下载两个依赖 npm install file-save xlsx 创建两个文件 Blob.js与Export2Excel.js 参考地址:文件地址 在Export2Excel.js中修改引入地址,注 ...

  8. LeetCode113. 路径总和 II

    原题链接 1 class Solution: 2 def pathSum(self, root: TreeNode, sum: int) -> List[List[int]]: 3 ans,tm ...

  9. javascript处理HTML的Encode(转码)和解码(Decode)

    HTML的Encode(转码)和解码(Decode)在平时的开发中也是经常要处理的,在这里总结了使用javascript处理HTML的Encode(转码)和解码(Decode)的常用方式 一.用浏览器 ...

  10. C#无损压缩图片

    /// <summary> /// 根据指定尺寸得到按比例缩放的尺寸,返回true表示以更改尺寸 /// </summary> /// <param name=" ...