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、条件变量-生产消费者模型 条件变量是一种线程同步机制。当条件不满足时,相关线程被一直阻塞,直到某种条件出现,这些线 程才会被唤醒。 C11 的条件变量提供了两个类: condition_variable:只支持与普通 mutex 搭配&…...
Ubuntu系统入门指南:常用命令详解
Ubuntu系统入门指南:常用命令详解 引言 Ubuntu是一个基于Linux内核的开源操作系统,由Canonical公司和社区共同开发和维护。它以易用性、稳定性和广泛的软件支持而著称,广泛应用于个人电脑、服务器和云计算环境。对于新手来说,掌…...
keep-alive缓存不了iframe
最近做了个项目,其中有个页面是由 iframe 嵌套了一个另外的页面,在运行的过程中发现 KeepAlive 并不生效,每次切换路由都会触发 iframe 页面的重新渲染,代码如下: <router-view v-slot"{ Component }">…...
Gradio快速部署构建AIGC的web应用 ,python
Gradio快速部署构建AIGC的web应用 ,python Gradio开源项目链接: https://github.com/gradio-app/gradiohttps://github.com/gradio-app/gradio (1)python的pip安装: pip install gradio (2)写…...
《职教论坛》
《职教论坛》投稿须知 《职教论坛》为全国中文核心期刊。为进一步提高期刊的规范化和标准化,也可使作者投搞有规可循,特对来稿提出如下要求: 一、稿件应有创新内容,应观点明确、资料准确、结构严谨、表述清楚、文字简明ÿ…...
JZ2440开发板——S3C2440的时钟体系
参考博客 (1)S3C2440-裸机篇-05 | S3C2440时钟体系详解(FCLK、PCLK、HCLK) 一、三种时钟(FCLK、HCLK、PCLK) 如下图所示,S3C2440的时钟控制逻辑,给整个芯片提供三种时钟࿱…...
[数据集][目标检测]男女性别检测数据集VOC+YOLO格式9769张2类别
数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):9769 标注数量(xml文件个数):9769 标注数量(txt文件个数):9769 标注…...
static 初始化报错
在 C 或 C 中,当你在函数中使用 static 关键字初始化一个局部变量时,编译器要求初始化器(initializer)是编译时常量。如果你尝试用动态计算的值初始化 static 变量(如函数参数或运行时生成的值),…...
3D Gaussian Splatting 论文学习
概述 目前比较常见的渲染方法大致可以分为2种: 将场景中的物体投影到渲染平面:传统的渲染管线就是这种方式,主要针对Mesh数据,可以将顶点直接投影成2D的形式,配合光栅化、深度测试、Alpha混合等就可以得到渲染的图像…...
MySQL 安全机制全面解析
在如今的数字化时代,数据库安全 变得越来越重要。为了防止对数据库进行非法操作,MySQL 定义了一套完整的安全机制,包括用户管理、权限管理 和 角色管理。本文将为你深入浅出地介绍这三大安全机制,帮助你轻松掌握MySQL的安全管…...
vue原理分析(十四)研究new Vue()中的 initProvide
在Vue.prototype._init 中有一些init函数,今天我们来研究这些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接口进行控制是嵌入式中非常重要的一点,就像冯诺依曼原理说的一样,一个计算机最起码要有输入输出吧,我们有了信息的接收和处理,那我们就要有输出。 我们在开发板上一般都是使用开发板的GPIO接口…...
S3C2440开发板点亮LED灯+PWM定时器
目录 GPIO引脚和寄存器概述 点亮LED灯步骤 1.配置GPIO 2.点亮LED 设置引脚为输出 控制引脚电平 完整代码 PWM GPIO引脚和寄存器概述 GPIO端口: S3C2440的GPIO引脚可被配置为输入或输出(控制LED的引脚通常配置为输出模式)。寄存器&#…...
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 释放所有缓存(页缓存、目录项和inode缓存) sudo sync…...
rust学习笔记
参考资料:https://doc.rust-lang.org/book/ch01-02-hello-world.html 一、 编译与运行 在 Rust 中,编译和运行代码的常用命令是使用 cargo,这是 Rust 的包管理和构建工具。以下是使用 cargo 和 rustc(Rust 编译器)的具…...
【有啥问啥】复习变分下界即证据下界(Evidence Lower Bound, ELBO):原理与应用
复习变分下界即证据下界(Evidence Lower Bound, ELBO):原理与应用 变分下界(Variational Lower Bound),也称为“证据下界”(Evidence Lower Bound, ELBO),是概率模型中的…...
Linux shell编程学习笔记78:cpio命令——文件和目录归档工具(上)
0 前言 在Linux系统中,除了tar命令,我们还可以使用cpio命令来进行文件和目录的归档。 1 cpio命令的功能,帮助信息,格式,选项和参数说明 1.1 cpio命令的功能 cpio 名字来自 "copy in, copy out"…...
为什么在 JSON 序列化中不使用 transient
有些小伙伴发现了,明明在返回的实体类中指定了属性为transient。为什么前端得到的返回json中还是有这个属性的值? 类: private String name; private transient String password;返回结果: { name:"刘大大", password:…...
K8S - Volume - NFS 卷的简介和使用
在之前的文章里已经介绍了 K8S 中两个简单卷类型 hostpath 和 emptydir k8s - Volume 简介和HostPath的使用 K8S - Emptydir - 取代ELK 使用fluentd 构建logging saidcar 但是这两种卷都有同1个限制, 就是依赖于 k8s nodes的空间 如果某个service pod中需要的vol…...
stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...
FFmpeg 低延迟同屏方案
引言 在实时互动需求激增的当下,无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作,还是游戏直播的画面实时传输,低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架,凭借其灵活的编解码、数据…...
中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试
作者:Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位:中南大学地球科学与信息物理学院论文标题:BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接:https://arxiv.…...
23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...
C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...
浪潮交换机配置track检测实现高速公路收费网络主备切换NQA
浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求,本次涉及的主要是收费汇聚交换机的配置,浪潮网络设备在高速项目很少,通…...
【从零学习JVM|第三篇】类的生命周期(高频面试题)
前言: 在Java编程中,类的生命周期是指类从被加载到内存中开始,到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期,让读者对此有深刻印象。 目录 …...
Chromium 136 编译指南 Windows篇:depot_tools 配置与源码获取(二)
引言 工欲善其事,必先利其器。在完成了 Visual Studio 2022 和 Windows SDK 的安装后,我们即将接触到 Chromium 开发生态中最核心的工具——depot_tools。这个由 Google 精心打造的工具集,就像是连接开发者与 Chromium 庞大代码库的智能桥梁…...
