C++ 互斥锁的使用
mutex
std::mutex 是C++标准库中用于线程同步的互斥锁机制,主要用于保护共享资源,避免多个线程同时访问导致的竞态条件。
它提供了以下功能:
-
加锁(
lock):阻塞当前线程,直到获取锁。 -
解锁(
unlock):释放锁,允许其他线程获取锁。 -
尝试加锁(
try_lock):尝试获取锁,如果锁已被占用则立即返回。
使用全局锁案例
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
mutex mtx; // 定义一个全局互斥锁
int shared_data = 0;void increment() {for (int i = 0; i < 10000; i++) {mtx.lock(); // 加锁shared_data++;mtx.unlock(); // 解锁}
}int main() {thread t1(increment);thread t2(increment);t1.join();t2.join();cout << "共享数据: " << shared_data << endl; // 输出应该是20000return 0;
}
使用传参锁案例
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
int shared_data = 0;void increment(mutex& mtx) {for (int i = 0; i < 10000; i++) {mtx.lock(); // 加锁shared_data++;mtx.unlock(); // 解锁}
}int main() {mutex mtx; // 使用传参互斥锁thread t1(increment,ref(mtx));//需要使用 ref 包装锁,不然会报错thread t2(increment,ref(mtx));t1.join();t2.join();cout << "共享数据: " << shared_data << endl; // 输出应该是20000return 0;
} timed_mutex
std::timed_mutex 是C++11引入的一种互斥锁类型,用于多线程编程中控制对共享资源的并发访问。它提供了超时机制,允许线程在尝试获取锁时设置一个时间限制,从而避免无限等待锁释放,降低死锁风险。
-
try_lock_for(duration):尝试在指定的时间内获取锁,如果超时则返回false。 -
try_lock_until(time_point):尝试在指定的时间点之前获取锁,如果超时则返回false。
#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
using namespace std;
timed_mutex mtx;void func(int id) {chrono::seconds timeout(3); // 设置超时时间为3秒if (mtx.try_lock_for(timeout)) {cout << "线程 " << id << " 成功获取锁,执行临界区代码..." << endl;this_thread::sleep_for(chrono::seconds(5)); // 模拟工作负载mtx.unlock();}else {cout << "线程 " << id << " 超时未能获取锁,继续执行其他任务..." << endl;}
}int main() {thread t1(func, 1);thread t2(func, 2);t1.join();t2.join();return 0;
} recursive_mutex
- 调⽤⽅线程在从它成功调⽤ lock 或 try_lock 开始的时期⾥占有 recursive_mutex。此时期之内,线程可以进⾏对 lock 或 try_lock 的附加调⽤。所有权的时期在线程进⾏匹配次数的 unlock 调⽤时结束。
- 线程占有 recursive_mutex 时,若其他所有线程试图要求 recursive_mutex 的所有权,则它们将阻塞(对于调⽤ lock)或收到 false 返回值(对于调⽤ try_lock)。
#include <iostream>
#include <mutex>
#include <thread>
using namespace std;
recursive_mutex rec_mutex;void recursiveFunction(int count) {if (count <= 0) return;rec_mutex.lock();cout << "Lock acquired. Count: " << count << endl;// 递归调用recursiveFunction(count - 1);rec_mutex.unlock();cout << "Lock released. Count: " << count << endl;
}int main() {thread t(recursiveFunction, 3);t.join();return 0;
} lock_guard
std::lock_guard 是 C++ 标准库中用于管理互斥锁的 RAII(Resource Acquisition Is Initialization,资源获取即初始化)工具。它通过构造函数自动获取互斥锁,并在析构函数中自动释放锁,从而确保互斥锁的正确管理,避免因忘记解锁而导致的死锁问题。
主要特性
-
自动管理锁:通过构造函数获取锁,析构函数释放锁,无需手动调用
lock()和unlock()。 -
防止死锁:确保即使在异常情况下也能正确释放锁。
-
不可移动和复制:std::lock_guard 不支持拷贝或移动构造,确保锁的唯一性。
构造函数
explicit lock_guard(MutexType& mutex, std::adopt_lock_t tag = std::defer_lock); -
MutexType& mutex:要管理的互斥锁对象,如mutex,timed_mutex,recersive_mutex。
-
std::adopt_lock_t tag:可选参数,表示当前线程已经持有锁,默认值是 std::defer_lock,表示 lock_guard 会在构造时自动尝试加锁。如果传入 std::adopt_lock,则表示当前线程已经通过其他方式(例如 std::lock 或手动调用 lock())持有锁,lock_guard 不会再次尝试加锁,而是直接接管锁的管理。
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
mutex mtx;
void print_block(int n, char c) {lock_guard<mutex> lock(mtx); // 默认加锁,无需手动解锁for (int i = 0; i < n; ++i) {cout << c;}cout << '\n';
}int main() {thread t1(print_block, 50, '*');thread t2(print_block, 50, '$');t1.join();t2.join();return 0;
} unique_lock
主要特性
-
延迟加锁:可以在构造时不立即加锁,而是通过显式调用
lock()或unlock()来控制锁的获取和释放。 -
支持条件变量:可以与
std::condition_variable配合使用,支持线程间的同步。 -
可选锁管理:可以选择是否持有锁,或者在构造时直接接管已持有的锁。
-
支持多种互斥锁类型:可以与
std::mutex、std::recursive_mutex、std::timed_mutex和std::shared_mutex配合使用。
构造函数
template <typename Mutex>
unique_lock() noexcept; // 默认构造,不绑定任何锁template <typename Mutex>
explicit unique_lock(Mutex& m, std::defer_lock_t); // 延迟加锁template <typename Mutex>
unique_lock(Mutex& m, std::try_to_lock_t); // 尝试加锁,失败时不阻塞template <typename Mutex>
unique_lock(Mutex& m, std::adopt_lock_t); // 假设已持有锁,不加锁template <typename Mutex>
unique_lock(Mutex& m); // 默认加锁template <typename Mutex>
unique_lock(Mutex& m, std::unique_lock<Mutex>&& other); // 移动构造 主要成员函数
-
lock():尝试加锁,如果锁已被占用则阻塞。 -
try_lock():尝试加锁,如果锁已被占用则立即返回false。 -
unlock():释放锁。 -
owns_lock():检查是否持有锁。 -
mutex():获取绑定的互斥锁对象。 -
release():释放锁的所有权,但不释放锁本身。 -
swap():交换两个std::unique_lock的状态。
lock和try_lock
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
mutex mtx1;
mutex mtx2;
void worker() {lock(mtx1, mtx2); // 同时锁定两个锁unique_lock<mutex> lock1(mtx1, std::adopt_lock); // 需要创建 unique_lock 自动释放锁unique_lock<mutex> lock2(mtx2, std::adopt_lock);cout << "线程进入临界区..." << endl;this_thread::sleep_for(chrono::seconds(1)); // 模拟工作负载cout << "线程退出临界区..." << endl;
}int main() {thread t1(worker);thread t2(worker);t1.join();t2.join();return 0;
} call_once
std::call_once 是 C++11 引入的一个函数模板,用于确保某个操作(如初始化或配置加载)在多线程环境中只被调用一次。它结合了 std::once_flag,能够高效地实现线程安全的单次执行。
基本用法
std::call_once 的函数原型如下:
template <class Callable, class... Args>
void call_once(std::once_flag& flag, Callable&& f, Args&&... args); -
std::once_flag:一个标记对象,用于记录操作是否已经执行过。 -
Callable&& f:需要执行的可调用对象(如函数、lambda 表达式、函数对象等)。 -
Args&&... args:传递给可调用对象的参数。
工作原理
-
首次调用:如果 std::once_flag 标记的操作尚未执行,则 std::call_once 会调用
f,并设置 flag 标记为“已执行”。 -
后续调用:如果 flag 已标记为“已执行”,则后续调用 std::call_once 的线程会直接跳过 f 的执行。
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;std::once_flag flag;
void init(int id) {cout << "Initialization function called once." << endl;cout << "thread " << id << " is running." << endl;
}void worker(int id) {call_once(flag, init,id); // 确保 init 只被调用一次
}int main() {thread t1(worker, 1);thread t2(worker, 2);thread t3(worker, 3);t1.join();t2.join();t3.join();return 0;
} 相关文章:
C++ 互斥锁的使用
mutex std::mutex 是C标准库中用于线程同步的互斥锁机制,主要用于保护共享资源,避免多个线程同时访问导致的竞态条件。 它提供了以下功能: 加锁(lock):阻塞当前线程,直到获取锁。 解锁&#…...
【Elasticsearch】Retrieve inner hits获取嵌套查询的具体的嵌套文档来源,以及父子文档的来源
Retrieve inner hits 是 Elasticsearch 中的一个功能,用于在嵌套查询或父子查询中,返回导致主文档匹配的具体嵌套对象或子/父文档的详细信息,帮助用户更直观地理解查询结果的来源。 在 Elasticsearch 中,Retrieve inner hits是一…...
C语言中的typedef关键字详解
C语言中的typedef关键字详解 在C语言编程中,typedef 关键字是一个非常实用的特性,它可以帮助我们创建新的类型名,从而简化代码,提高可读性。本文将详细解析typedef的使用方法、场景以及注意事项。 1. typedef简介 typedef 是Ty…...
怎麼利用靜態ISP住宅代理在指紋流覽器中管理社媒帳號?
靜態ISP住宅代理是一種基於真實住宅IP的代理服務。這類代理IP通常由互聯網服務提供商(ISP)分配,具有非常高的真實性,與普通數據中心代理相比,更不容易被平臺檢測到為“虛假IP”或“代理IP”,靜態ISP住宅代理…...
【多语言生态篇一】【DeepSeek×Java:Spring Boot微服务集成全栈指南 】
(手把手带你从零实现AI能力调用,万字长文预警,建议收藏实操) 一、环境准备:别输在起跑线上 1.1 硬件软件全家桶 JDK版本:必须 ≥17(Spring Boot 3.2+强制要求,低版本直接报错)IDE推荐:IntelliJ IDEA终极版(社区版缺Spring AI插件支持)构建工具:Maven 3.9+ / Grad…...
IOS UITextField 无法隐藏键盘问题
设置UITextField 键盘按钮返回键为“完成”,即return key 设置done .m代码设置代理 //设置代理协议 UITextFieldDelegate, self.mobileTextField.delegate self; ///点击完成键隐藏键盘 - (BOOL)textFieldShouldReturn:(UITextField *)textField{//取…...
einops测试
文章目录 1. einops2. code3. pytorch 1. einops einops 主要是通过爱因斯坦标记法来处理张量矩阵的库,让矩阵处理上非常简单。 conda : conda install conda-forge::einopspython: 2. code import torch import torch.nn as nn import torch.nn.functional as…...
25轻化工程研究生复试面试问题汇总 轻化工程专业知识问题很全! 轻化工程复试全流程攻略 轻化工程考研复试真题汇总
轻化工程复试心里没谱?学姐带你玩转面试准备! 是不是总觉得老师会问些刁钻问题?别焦虑!其实轻化工程复试套路就那些,看完这篇攻略直接掌握复试通关密码!文中有重点面试题可直接背~ 目录 一、这些行为赶紧避…...
小米路由器 AX3000T 降级后无法正常使用,解决办法
问题描述 买了个 AX3000T 路由器,想安装 OpenWRT 或者 安装 Clash 使用,看教程说是需要降级到 v1.0.47 版本。 结果刷机之后路由器无法打开了,一直黄灯亮,中间灭一下,又是黄灯长亮,没有 WIFI 没有连接。以…...
qt5实现表盘的旋转效果,通过提升QLabel类
因为工作需要,需要实现温度的表盘展示效果 实现思路: 通过提示声QLabel控价类,实现报盘的旋转和展示效果 1. 编写一个QLabel的类MyQLabel,实现两个方法 1. void paintEvent(QPaintEvent *event); //重绘函数 2. void valueChanged(int va…...
【HeadFirst系列之HeadFirst设计模式】第7天之命令模式:封装请求,轻松实现解耦!
命令模式:封装请求,轻松实现解耦! 大家好!今天我们来聊聊设计模式中的命令模式(Command Pattern)。如果你曾经需要将请求封装成对象,或者希望实现请求的撤销、重做等功能,那么命令模…...
HTTPS(下)
主要讲加密算法RSA,ECDHE TLS的握手涉及四次通信,根据不同的密钥交换算法,TLS 握手流程也会不一样的,现在常用的密钥交换算法有两种:RSA 算法和 ECDHE 算法 正常情况下,需要先TCP三次握手后进行TLS四次握手…...
vue2 和 vue3 中 computer 计算属性的用法
Vue 2 中的 computed 在 Vue 2 中,计算属性是响应式的,并且基于 getter 进行缓存,只有依赖的响应式数据发生变化时才会重新计算。 基本用法 <template><div><p>原始消息:{{ message }}</p><p>反…...
【STM32学习】标准库实现STM32 ADC采集1路、2路、多路
目录 ADC采集 ADC配置步骤 STM32F103C8T6的ADC 输入通道 编辑 1路ADC(A4 ADC 通道4) 1路ADC源码代码链接: 2路ADC(A4 ADC 通道4、A5 ADC 通道5)基于DMA实现 多路ADC实现采集 ADC采集 ADC配置步骤 使能GPIO…...
【STM32】外部时钟|红外反射光电开关
1.外部时钟 单片机如何对外部触发进行计数?先看一下内部时钟,内部时钟是接在APB1和APB2时钟线上的,APB1,APB2来自stm32单片机内部的脉冲信号,也叫内部时钟。我们用来定时。同样我们可以把外部的信号接入单片机,来对其…...
【语音科学计算器】当前汇率
JSON_MARKER_HORN{“base”:“USD”,“rates”:{“EUR”:0.9758,“JPY”:157.68,“GBP”:0.8190,“CNY”:7.3327,“HKD”:7.7872,“AUD”:1.6260,“CAD”:1.4422,“CHF”:0.9157,“SGD”:1.3714,“KRW”:1473.05,“NZD”:1.7992,“THB”:34.54,“MYR”:4.4930,“PHP”:57.32,“…...
PHP post 数据丢失问题
max_input_vars是PHP配置选项之一,用于设置一个请求中允许的最大输入变量数。它指定了在处理POST请求或者通过URL传递的参数时,PHP脚本能够接收和处理的最大变量数量。 max_input_vars的默认值是1000,意味着一个请求中最多可以包含1000个输入…...
【云服务器】云服务器内存不够用,开启SWAP交换分区
交换分区(Swap) 1.创建 2GB Swap 文件 sudo fallocate -l 2G /swapfile (如果 fallocate 不支持,可以用 dd 命令) sudo dd if/dev/zero of/swapfile bs1M count2048 2.设置 Swap 权限 sudo chmod 600 /swapfile…...
未来SLAM的研究方向和热点
SLAM(Simultaneous Localization and Mapping)是同时定位与地图构建的缩写,指的是机器人或设备在一个未知环境中一边进行自我定位,一边构建出环境的地图。SLAM广泛应用于机器人、自动驾驶、无人机等领域,涉及多个研究方…...
Orange 单体架构 - 快速启动
1 后端服务 1.1 基础设施 组件说明版本MySQLMySQL数据库服务5.7/8JavaJava17redis-stackRedis向量数据库最新版本Node安装Node22.11.0 1.2 orange-dependencies-parent 项目Maven依赖版本管理 1.2.1 项目克隆 GitHub git clone https://github.com/hengzq/orange-depende…...
手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频
使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...
页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
Mac软件卸载指南,简单易懂!
刚和Adobe分手,它却总在Library里给你写"回忆录"?卸载的Final Cut Pro像电子幽灵般阴魂不散?总是会有残留文件,别慌!这份Mac软件卸载指南,将用最硬核的方式教你"数字分手术"࿰…...
EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...
零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...
C++使用 new 来创建动态数组
问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...
