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…...
循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...
零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...
k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
华硕a豆14 Air香氛版,美学与科技的馨香融合
在快节奏的现代生活中,我们渴望一个能激发创想、愉悦感官的工作与生活伙伴,它不仅是冰冷的科技工具,更能触动我们内心深处的细腻情感。正是在这样的期许下,华硕a豆14 Air香氛版翩然而至,它以一种前所未有的方式&#x…...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...
MySQL 部分重点知识篇
一、数据库对象 1. 主键 定义 :主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 :确保数据的完整性,便于数据的查询和管理。 示例 :在学生信息表中,学号可以作为主键ÿ…...
云安全与网络安全:核心区别与协同作用解析
在数字化转型的浪潮中,云安全与网络安全作为信息安全的两大支柱,常被混淆但本质不同。本文将从概念、责任分工、技术手段、威胁类型等维度深入解析两者的差异,并探讨它们的协同作用。 一、核心区别 定义与范围 网络安全:聚焦于保…...
