当前位置: 首页 > news >正文

C++学习笔记(21)

243、条件变量-生产消费者模型
条件变量是一种线程同步机制。当条件不满足时,相关线程被一直阻塞,直到某种条件出现,这些线
程才会被唤醒。
C++11 的条件变量提供了两个类:
condition_variable:只支持与普通 mutex 搭配,效率更高。
condition_variable_any:是一种通用的条件变量,可以与任意 mutex 搭配(包括用户自定义的锁
类型)。
包含头文件:<condition_variable> 一、condition_variable 类
主要成员函数:
1)condition_variable() 默认构造函数。
2)condition_variable(const condition_variable &)=delete 禁止拷贝。
3)condition_variable& condition_variable::operator=(const condition_variable &)=delete
禁止赋值。
4)notify_one() 通知一个等待的线程。
5)notify_all() 通知全部等待的线程。
6)wait(unique_lock<mutex> lock) 阻塞当前线程,直到通知到达。
7)wait(unique_lock<mutex> lock,Pred pred) 循环的阻塞当前线程,直到通知到达且谓词满足。
8)wait_for(unique_lock<mutex> lock,时间长度)
9)wait_for(unique_lock<mutex> lock,时间长度,Pred pred)
10)wait_until(unique_lock<mutex> lock,时间点)
11)wait_until(unique_lock<mutex> lock,时间点,Pred pred)
二、unique_lock 类
template <class Mutex> class unique_lock 是模板类,模板参数为互斥锁类型。
unique_lock 和 lock_guard 都是管理锁的辅助类,都是 RAII 风格(在构造时获得锁,在析构时释放
锁)。它们的区别在于:为了配合 condition_variable,unique_lock 还有 lock()和 unlock()成员函数。
示例 1:
#include <iostream>
#include <string>
#include <thread> // 线程类头文件。
#include <mutex> // 互斥锁类的头文件。
#include <deque> // deque 容器的头文件。
#include <queue> // queue 容器的头文件。
#include <condition_variable> // 条件变量的头文件。
using namespace std;
class AA
{
mutex m_mutex; // 互斥锁。
condition_variable m_cond; // 条件变量。
queue<string, deque<string>> m_q; // 缓存队列,底层容器用 deque。
public:
void incache(int num) // 生产数据,num 指定数据的个数。
{
lock_guard<mutex> lock(m_mutex); // 申请加锁。
for (int ii=0 ; ii<num ; ii++)
{
static int bh = 1; // 超女编号。
string message = to_string(bh++) + "号超女"; // 拼接出一个数据。
m_q.push(message); // 把生产出来的数据入队。
}
m_cond.notify_one(); // 唤醒一个被当前条件变量阻塞的线程。
}
void outcache() // 消费者线程任务函数。
{
while (true)
{
string message;
{
// 把互斥锁转换成 unique_lock<mutex>,并申请加锁。
unique_lock<mutex> lock(m_mutex);
while (m_q.empty()) // 如果队列空,进入循环,否则直接处理数据。必须用
循环,不能用 if
m_cond.wait(lock); // 等待生产者的唤醒信号。
// 数据元素出队。
message = m_q.front(); m_q.pop();
}
// 处理出队的数据(把数据消费掉)。
this_thread::sleep_for(chrono::milliseconds(1)); // 假设处理数据需要 1 毫秒。
cout << "线程:" << this_thread::get_id() << "," << message << endl;
}
}
};
int main()
{
AA aa;
thread t1(&AA::outcache, &aa); // 创建消费者线程 t1。
thread t2(&AA::outcache, &aa); // 创建消费者线程 t2。
thread t3(&AA::outcache, &aa); // 创建消费者线程 t3。
this_thread::sleep_for(chrono::seconds(2)); // 休眠 2 秒。
aa.incache(3); // 生产 3 个数据。
this_thread::sleep_for(chrono::seconds(3)); // 休眠 3 秒。
aa.incache(5); // 生产 5 个数据。
t1.join(); // 回收子线程的资源。
t2.join();
t3.join();
}
示例 2:
#include <iostream>
#include <string>
#include <thread> // 线程类头文件。
#include <mutex> // 互斥锁类的头文件。
#include <deque> // deque 容器的头文件。
#include <queue> // queue 容器的头文件。
#include <condition_variable> // 条件变量的头文件。
using namespace std;
class AA
{
mutex m_mutex; // 互斥锁。
condition_variable m_cond; // 条件变量。
queue<string, deque<string>> m_q; // 缓存队列,底层容器用 deque。
public:
void incache(int num) // 生产数据,num 指定数据的个数。
{
lock_guard<mutex> lock(m_mutex); // 申请加锁。
for (int ii=0 ; ii<num ; ii++)
{
static int bh = 1; // 超女编号。
string message = to_string(bh++) + "号超女"; // 拼接出一个数据。
m_q.push(message); // 把生产出来的数据入队。
}
//m_cond.notify_one(); // 唤醒一个被当前条件变量阻塞的线程。
m_cond.notify_all(); // 唤醒全部被当前条件变量阻塞的线程。
}
void outcache() { // 消费者线程任务函数。
while (true) {
// 把互斥锁转换成 unique_lock<mutex>,并申请加锁。
unique_lock<mutex> lock(m_mutex);
// 条件变量虚假唤醒:消费者线程被唤醒后,缓存队列中没有数据。
//while (m_q.empty()) // 如果队列空,进入循环,否则直接处理数据。必须用循
环,不能用 if
// m_cond.wait(lock); // 1)把互斥锁解开;2)阻塞,等待被唤醒;3)给互斥
锁加锁。
m_cond.wait(lock, [this] { return !m_q.empty(); });
// 数据元素出队。
string message = m_q.front(); m_q.pop();
cout << "线程:" << this_thread::get_id() << "," << message << endl;
lock.unlock(); // 手工解锁。
// 处理出队的数据(把数据消费掉)。
this_thread::sleep_for(chrono::milliseconds(1)); // 假设处理数据需要 1 毫秒。
}
}
};
int main()
{
AA aa;
thread t1(&AA::outcache, &aa); // 创建消费者线程 t1。
thread t2(&AA::outcache, &aa); // 创建消费者线程 t2。
thread t3(&AA::outcache, &aa); // 创建消费者线程 t3。
this_thread::sleep_for(chrono::seconds(2)); // 休眠 2 秒。
aa.incache(2); // 生产 2 个数据。
this_thread::sleep_for(chrono::seconds(3)); // 休眠 3 秒。
aa.incache(5); // 生产 5 个数据。
t1.join(); // 回收子线程的资源。
t2.join();
t3.join();
}
244、原子类型 atomic
C++11 提供了 atomic<T>模板类(结构体),用于支持原子类型,模板参数可以是 bool、char、i
nt、long、long long、指针类型(不支持浮点类型和自定义数据类型)。
原子操作由 CPU 指令提供支持,它的性能比锁和消息传递更高,并且,不需要程序员处理加锁和释
放锁的问题,支持修改、读取、交换、比较并交换等操作。
头文件:#include <atomic>
构造函数:
atomic() noexcept = default; // 默认构造函数。
atomic(T val) noexcept; // 转换函数。
atomic(const atomic&) = delete; // 禁用拷贝构造函数。
赋值函数:
atomic& operator=(const atomic&) = delete; // 禁用赋值函数。
常用函数:
void store(const T val) noexcept; // 把 val 的值存入原子变量。
T load() noexcept; // 读取原子变量的值。
T fetch_add(const T val) noexcept; // 把原子变量的值与 val 相加,返回原值。
T fetch_sub(const T val) noexcept; // 把原子变量的值减 val,返回原值。
T exchange(const T val) noexcept; // 把 val 的值存入原子变量,返回原值。
T compare_exchange_strong(T &expect,const T val) noexcept; // 比较原子变量的值和预期
值 expect,如果当两个值相等,把 val 存储到原子变量中,函数返回 true;如果当两个值不相等,用原
子变量的值更新预期值,函数返回 false。CAS 指令。
bool is_lock_free(); // 查询某原子类型的操作是直接用 CPU 指令(返回 true),还是编译器内部
的锁(返回 false)。
原子类型的别名:
注意:
 atomic<T>模板类重载了整数操作的各种运算符。
 atomic<T>模板类的模板参数支持指针,但不表示它所指向的对象是原子类型。
 原子整型可以用作计数器,布尔型可以用作开关。
 CAS 指令是实现无锁队列基础。
示例:
#include <iostream>
#include <atomic> // 原子类型的头文件。
using namespace std;
int main()
{
atomic<int> a = 3; // atomic(T val) noexcept; // 转换函数。
cout << "a=" << a.load() << endl; // 读取原子变量 a 的值。输出:a=3
a.store(8); // 把 8 存储到原子变量中。
cout << "a=" << a.load() << endl; // 读取原子变量 a 的值。 输出:a=8
int old; // 用于存放原值。
old = a.fetch_add(5); // 把原子变量 a 的值与 5 相加,返回原值。
cout << "old = " << old <<",a = " << a.load() << endl; // 输出:old=8,a=13
old = a.fetch_sub(2); // 把原子变量 a 的值减 2,返回原值。
cout << "old = " << old << ",a = " << a.load() << endl; // 输出:old=13,a=11
atomic<int> ii = 3; // 原子变量
int expect = 4; // 期待值
int val = 5; // 打算存入原子变量的值
// 比较原子变量的值和预期值 expect,
// 如果当两个值相等,把 val 存储到原子变量中;
// 如果当两个值不相等,用原子变量的值更新预期值。
// 执行存储操作时返回 true,否则返回 false。
bool bret = ii.compare_exchange_strong(expect, val);
cout << "bret=" << bret << endl;
cout << "ii=" << ii << endl;
cout << "expect=" << expect << endl;
}
 

相关文章:

C++学习笔记(21)

243、条件变量-生产消费者模型 条件变量是一种线程同步机制。当条件不满足时&#xff0c;相关线程被一直阻塞&#xff0c;直到某种条件出现&#xff0c;这些线 程才会被唤醒。 C11 的条件变量提供了两个类&#xff1a; condition_variable&#xff1a;只支持与普通 mutex 搭配&…...

Ubuntu系统入门指南:常用命令详解

Ubuntu系统入门指南&#xff1a;常用命令详解 引言 Ubuntu是一个基于Linux内核的开源操作系统&#xff0c;由Canonical公司和社区共同开发和维护。它以易用性、稳定性和广泛的软件支持而著称&#xff0c;广泛应用于个人电脑、服务器和云计算环境。对于新手来说&#xff0c;掌…...

keep-alive缓存不了iframe

最近做了个项目&#xff0c;其中有个页面是由 iframe 嵌套了一个另外的页面&#xff0c;在运行的过程中发现 KeepAlive 并不生效&#xff0c;每次切换路由都会触发 iframe 页面的重新渲染&#xff0c;代码如下&#xff1a; <router-view v-slot"{ Component }">…...

Gradio快速部署构建AIGC的web应用 ,python

Gradio快速部署构建AIGC的web应用 &#xff0c;python Gradio开源项目链接&#xff1a; https://github.com/gradio-app/gradiohttps://github.com/gradio-app/gradio &#xff08;1&#xff09;python的pip安装&#xff1a; pip install gradio &#xff08;2&#xff09;写…...

《职教论坛》

《职教论坛》投稿须知 《职教论坛》为全国中文核心期刊。为进一步提高期刊的规范化和标准化&#xff0c;也可使作者投搞有规可循&#xff0c;特对来稿提出如下要求&#xff1a; 一、稿件应有创新内容&#xff0c;应观点明确、资料准确、结构严谨、表述清楚、文字简明&#xff…...

JZ2440开发板——S3C2440的时钟体系

参考博客 &#xff08;1&#xff09;S3C2440-裸机篇-05 | S3C2440时钟体系详解&#xff08;FCLK、PCLK、HCLK&#xff09; 一、三种时钟&#xff08;FCLK、HCLK、PCLK&#xff09; 如下图所示&#xff0c;S3C2440的时钟控制逻辑&#xff0c;给整个芯片提供三种时钟&#xff1…...

[数据集][目标检测]男女性别检测数据集VOC+YOLO格式9769张2类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;9769 标注数量(xml文件个数)&#xff1a;9769 标注数量(txt文件个数)&#xff1a;9769 标注…...

static 初始化报错

在 C 或 C 中&#xff0c;当你在函数中使用 static 关键字初始化一个局部变量时&#xff0c;编译器要求初始化器&#xff08;initializer&#xff09;是编译时常量。如果你尝试用动态计算的值初始化 static 变量&#xff08;如函数参数或运行时生成的值&#xff09;&#xff0c…...

3D Gaussian Splatting 论文学习

概述 目前比较常见的渲染方法大致可以分为2种&#xff1a; 将场景中的物体投影到渲染平面&#xff1a;传统的渲染管线就是这种方式&#xff0c;主要针对Mesh数据&#xff0c;可以将顶点直接投影成2D的形式&#xff0c;配合光栅化、深度测试、Alpha混合等就可以得到渲染的图像…...

MySQL 安全机制全面解析

‍ 在如今的数字化时代&#xff0c;数据库安全 变得越来越重要。为了防止对数据库进行非法操作&#xff0c;MySQL 定义了一套完整的安全机制&#xff0c;包括用户管理、权限管理 和 角色管理。本文将为你深入浅出地介绍这三大安全机制&#xff0c;帮助你轻松掌握MySQL的安全管…...

vue原理分析(十四)研究new Vue()中的 initProvide

在Vue.prototype._init 中有一些init函数&#xff0c;今天我们来研究这些init函数 Vue.prototype._init function (options) {......{initProxy(vm);}......initLifecycle(vm);initEvents(vm);initRender(vm);callHook$1(vm, beforeCreate, undefined, false /* setContext *…...

Qt控制开发板的LED

Qt控制开发板的LED 使用开发板的IO接口进行控制是嵌入式中非常重要的一点&#xff0c;就像冯诺依曼原理说的一样&#xff0c;一个计算机最起码要有输入输出吧&#xff0c;我们有了信息的接收和处理&#xff0c;那我们就要有输出。 我们在开发板上一般都是使用开发板的GPIO接口…...

S3C2440开发板点亮LED灯+PWM定时器

目录 GPIO引脚和寄存器概述 点亮LED灯步骤 1.配置GPIO 2.点亮LED 设置引脚为输出 控制引脚电平 完整代码 PWM GPIO引脚和寄存器概述 GPIO端口&#xff1a; S3C2440的GPIO引脚可被配置为输入或输出&#xff08;控制LED的引脚通常配置为输出模式&#xff09;。寄存器&#…...

S-Procedure的基本形式及使用

理论 Lemma 1. ( S- Procedure[ 34] ) : Define the quadratic func- \textbf{Lemma 1. ( S- Procedure[ 34] ) : Define the quadratic func- } Lemma 1. ( S- Procedure[ 34] ) : Define the quadratic func- tions w.r.t. x ∈ C M 1 \mathbf{x}\in\mathbb{C}^M\times1 x…...

free -h 查看内存free空间不足

free空间不足 大部分被buff/cache占用 解决办法一: 手动释放缓存 释放页缓存 sudo sync; sudo sysctl -w vm.drop_caches1 释放目录项和inode缓存 sudo sync; sudo sysctl -w vm.drop_caches2 释放所有缓存&#xff08;页缓存、目录项和inode缓存&#xff09; sudo sync…...

rust学习笔记

参考资料&#xff1a;https://doc.rust-lang.org/book/ch01-02-hello-world.html 一、 编译与运行 在 Rust 中&#xff0c;编译和运行代码的常用命令是使用 cargo&#xff0c;这是 Rust 的包管理和构建工具。以下是使用 cargo 和 rustc&#xff08;Rust 编译器&#xff09;的具…...

【有啥问啥】复习变分下界即证据下界(Evidence Lower Bound, ELBO):原理与应用

复习变分下界即证据下界&#xff08;Evidence Lower Bound, ELBO&#xff09;&#xff1a;原理与应用 变分下界&#xff08;Variational Lower Bound&#xff09;&#xff0c;也称为“证据下界”&#xff08;Evidence Lower Bound, ELBO&#xff09;&#xff0c;是概率模型中的…...

Linux shell编程学习笔记78:cpio命令——文件和目录归档工具(上)

0 前言 在Linux系统中&#xff0c;除了tar命令&#xff0c;我们还可以使用cpio命令来进行文件和目录的归档。 1 cpio命令的功能&#xff0c;帮助信息&#xff0c;格式&#xff0c;选项和参数说明 1.1 cpio命令的功能 cpio 名字来自 "copy in, copy out"&#xf…...

为什么在 JSON 序列化中不使用 transient

有些小伙伴发现了&#xff0c;明明在返回的实体类中指定了属性为transient。为什么前端得到的返回json中还是有这个属性的值&#xff1f; 类&#xff1a; private String name; private transient String password;返回结果&#xff1a; { name:"刘大大", password:…...

K8S - Volume - NFS 卷的简介和使用

在之前的文章里已经介绍了 K8S 中两个简单卷类型 hostpath 和 emptydir k8s - Volume 简介和HostPath的使用 K8S - Emptydir - 取代ELK 使用fluentd 构建logging saidcar 但是这两种卷都有同1个限制&#xff0c; 就是依赖于 k8s nodes的空间 如果某个service pod中需要的vol…...

边缘计算医疗风险自查APP开发方案

核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

linux arm系统烧录

1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 &#xff08;忘了有没有这步了 估计有&#xff09; 刷机程序 和 镜像 就不提供了。要刷的时…...

如何在看板中有效管理突发紧急任务

在看板中有效管理突发紧急任务需要&#xff1a;设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP&#xff08;Work-in-Progress&#xff09;弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中&#xff0c;设立专门的紧急任务通道尤为重要&#xff0c;这能…...

反射获取方法和属性

Java反射获取方法 在Java中&#xff0c;反射&#xff08;Reflection&#xff09;是一种强大的机制&#xff0c;允许程序在运行时访问和操作类的内部属性和方法。通过反射&#xff0c;可以动态地创建对象、调用方法、改变属性值&#xff0c;这在很多Java框架中如Spring和Hiberna…...

【HTTP三个基础问题】

面试官您好&#xff01;HTTP是超文本传输协议&#xff0c;是互联网上客户端和服务器之间传输超文本数据&#xff08;比如文字、图片、音频、视频等&#xff09;的核心协议&#xff0c;当前互联网应用最广泛的版本是HTTP1.1&#xff0c;它基于经典的C/S模型&#xff0c;也就是客…...

SpringTask-03.入门案例

一.入门案例 启动类&#xff1a; package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...

Typeerror: cannot read properties of undefined (reading ‘XXX‘)

最近需要在离线机器上运行软件&#xff0c;所以得把软件用docker打包起来&#xff0c;大部分功能都没问题&#xff0c;出了一个奇怪的事情。同样的代码&#xff0c;在本机上用vscode可以运行起来&#xff0c;但是打包之后在docker里出现了问题。使用的是dialog组件&#xff0c;…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习

禁止商业或二改转载&#xff0c;仅供自学使用&#xff0c;侵权必究&#xff0c;如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

GO协程(Goroutine)问题总结

在使用Go语言来编写代码时&#xff0c;遇到的一些问题总结一下 [参考文档]&#xff1a;https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现&#xff1a; 今天在看到这个教程的时候&#xff0c;在自己的电…...