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

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;
}

使用传参锁案例

如果线程对象传参给可调⽤对象时,使⽤引⽤⽅式传参,实参位置需要加上ref(obj)的⽅式,主要原因是thread本质还是系统库提供的线程API的封装,thread 构造取到参数包以后,要调⽤创建线程的API,还是需要将参数包打包成⼀个结构体传参过去,那么打包成结构体时,参考包对象就会拷⻉给结构体对象,使⽤ref传参的参数,会让结构体中的对应参数成员类型推导为引⽤,这样才能实现引⽤传参。
#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引入的一种互斥锁类型,用于多线程编程中控制对共享资源的并发访问。它提供了超时机制,允许线程在尝试获取锁时设置一个时间限制,从而避免无限等待锁释放,降低死锁风险。

timed_mutex 跟 mutex完全类似,只是额外提供 try_lock_for 和 try_lock_untile 的接⼝,这两个接⼝跟 try_lock 类似,只是他不会⻢上返回,⽽是直接进⼊阻塞,直到时间条件到了或者解锁了就会唤醒试图获取锁资源。
  • 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

recursive_mutex 跟 mutex 完全类似,允许同一个线程多次获取锁而不会导致死锁,这种互斥锁特别适用于递归函数或嵌套调用的场景。 recursive_mutex 提供排他性递归所有权语义:
  1. 调⽤⽅线程在从它成功调⽤ lock 或 try_lock 开始的时期⾥占有 recursive_mutex。此时期之内,线程可以进⾏对 lock 或 try_lock 的附加调⽤。所有权的时期在线程进⾏匹配次数的 unlock 调⽤时结束。
  2. 线程占有 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,资源获取即初始化)工具。它通过构造函数自动获取互斥锁,并在析构函数中自动释放锁,从而确保互斥锁的正确管理,避免因忘记解锁而导致的死锁问题。

主要特性

  1. 自动管理锁:通过构造函数获取锁,析构函数释放锁,无需手动调用 lock()unlock()

  2. 防止死锁:确保即使在异常情况下也能正确释放锁。

  3. 不可移动和复制: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

unique_lock也是C++11提供的⽀持RAII⽅式管理互斥锁资源的类,相⽐ lock_guard 他的功能⽀持
更丰富复杂。

主要特性

  1. 延迟加锁:可以在构造时不立即加锁,而是通过显式调用 lock()unlock() 来控制锁的获取和释放。

  2. 支持条件变量:可以与 std::condition_variable 配合使用,支持线程间的同步。

  3. 可选锁管理:可以选择是否持有锁,或者在构造时直接接管已持有的锁。

  4. 支持多种互斥锁类型:可以与 std::mutexstd::recursive_mutexstd::timed_mutexstd::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);  // 移动构造

主要成员函数

  1. lock():尝试加锁,如果锁已被占用则阻塞。

  2. try_lock():尝试加锁,如果锁已被占用则立即返回 false

  3. unlock():释放锁。

  4. owns_lock():检查是否持有锁。

  5. mutex():获取绑定的互斥锁对象。

  6. release():释放锁的所有权,但不释放锁本身。

  7. swap():交换两个 std::unique_lock 的状态。

lock和try_lock

lock是⼀个函数模板,可以⽀持对多个锁对象同时锁定,如果其中⼀个锁对象没有锁住,lock函数
会把已经锁定的对象解锁⽽进⼊阻塞,直到锁定所有的所有的对象,它的主要目的是避免死锁问题,通过一次性尝试锁定多个锁,确保所有锁都被成功锁定后才返回。
try_lock也是⼀个函数模板,尝试对多个锁对象进⾏同时尝试锁定,如果全部锁对象都锁定了,返 回-1,如果某⼀个锁对象尝试锁定失败,把已经锁定成功的锁对象解锁,并则返回这个对象的下标
(第⼀个参数对象,下标从0开始算)。
#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:传递给可调用对象的参数。

工作原理

  1. 首次调用:如果 std::once_flag 标记的操作尚未执行,则 std::call_once 会调用 f,并设置 flag 标记为“已执行”。

  2. 后续调用:如果 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标准库中用于线程同步的互斥锁机制&#xff0c;主要用于保护共享资源&#xff0c;避免多个线程同时访问导致的竞态条件。 它提供了以下功能&#xff1a; 加锁&#xff08;lock&#xff09;&#xff1a;阻塞当前线程&#xff0c;直到获取锁。 解锁&#…...

【Elasticsearch】Retrieve inner hits获取嵌套查询的具体的嵌套文档来源,以及父子文档的来源

Retrieve inner hits 是 Elasticsearch 中的一个功能&#xff0c;用于在嵌套查询或父子查询中&#xff0c;返回导致主文档匹配的具体嵌套对象或子/父文档的详细信息&#xff0c;帮助用户更直观地理解查询结果的来源。 在 Elasticsearch 中&#xff0c;Retrieve inner hits是一…...

C语言中的typedef关键字详解

C语言中的typedef关键字详解 在C语言编程中&#xff0c;typedef 关键字是一个非常实用的特性&#xff0c;它可以帮助我们创建新的类型名&#xff0c;从而简化代码&#xff0c;提高可读性。本文将详细解析typedef的使用方法、场景以及注意事项。 1. typedef简介 typedef 是Ty…...

怎麼利用靜態ISP住宅代理在指紋流覽器中管理社媒帳號?

靜態ISP住宅代理是一種基於真實住宅IP的代理服務。這類代理IP通常由互聯網服務提供商&#xff08;ISP&#xff09;分配&#xff0c;具有非常高的真實性&#xff0c;與普通數據中心代理相比&#xff0c;更不容易被平臺檢測到為“虛假IP”或“代理IP”&#xff0c;靜態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 键盘按钮返回键为“完成”&#xff0c;即return key 设置done .m代码设置代理 //设置代理协议 UITextFieldDelegate&#xff0c; self.mobileTextField.delegate self; ///点击完成键隐藏键盘 - (BOOL)textFieldShouldReturn:(UITextField *)textField{//取…...

einops测试

文章目录 1. einops2. code3. pytorch 1. einops einops 主要是通过爱因斯坦标记法来处理张量矩阵的库&#xff0c;让矩阵处理上非常简单。 conda : conda install conda-forge::einopspython: 2. code import torch import torch.nn as nn import torch.nn.functional as…...

25轻化工程研究生复试面试问题汇总 轻化工程专业知识问题很全! 轻化工程复试全流程攻略 轻化工程考研复试真题汇总

轻化工程复试心里没谱&#xff1f;学姐带你玩转面试准备&#xff01; 是不是总觉得老师会问些刁钻问题&#xff1f;别焦虑&#xff01;其实轻化工程复试套路就那些&#xff0c;看完这篇攻略直接掌握复试通关密码&#xff01;文中有重点面试题可直接背~ 目录 一、这些行为赶紧避…...

小米路由器 AX3000T 降级后无法正常使用,解决办法

问题描述 买了个 AX3000T 路由器&#xff0c;想安装 OpenWRT 或者 安装 Clash 使用&#xff0c;看教程说是需要降级到 v1.0.47 版本。 结果刷机之后路由器无法打开了&#xff0c;一直黄灯亮&#xff0c;中间灭一下&#xff0c;又是黄灯长亮&#xff0c;没有 WIFI 没有连接。以…...

qt5实现表盘的旋转效果,通过提升QLabel类

因为工作需要&#xff0c;需要实现温度的表盘展示效果 实现思路&#xff1a; 通过提示声QLabel控价类&#xff0c;实现报盘的旋转和展示效果 1. 编写一个QLabel的类MyQLabel,实现两个方法 1. void paintEvent(QPaintEvent *event); //重绘函数 2. void valueChanged(int va…...

【HeadFirst系列之HeadFirst设计模式】第7天之命令模式:封装请求,轻松实现解耦!

命令模式&#xff1a;封装请求&#xff0c;轻松实现解耦&#xff01; 大家好&#xff01;今天我们来聊聊设计模式中的命令模式&#xff08;Command Pattern&#xff09;。如果你曾经需要将请求封装成对象&#xff0c;或者希望实现请求的撤销、重做等功能&#xff0c;那么命令模…...

HTTPS(下)

主要讲加密算法RSA&#xff0c;ECDHE TLS的握手涉及四次通信&#xff0c;根据不同的密钥交换算法&#xff0c;TLS 握手流程也会不一样的&#xff0c;现在常用的密钥交换算法有两种&#xff1a;RSA 算法和 ECDHE 算法 正常情况下&#xff0c;需要先TCP三次握手后进行TLS四次握手…...

vue2 和 vue3 中 computer 计算属性的用法

Vue 2 中的 computed 在 Vue 2 中&#xff0c;计算属性是响应式的&#xff0c;并且基于 getter 进行缓存&#xff0c;只有依赖的响应式数据发生变化时才会重新计算。 基本用法 <template><div><p>原始消息&#xff1a;{{ message }}</p><p>反…...

【STM32学习】标准库实现STM32 ADC采集1路、2路、多路

目录 ADC采集 ADC配置步骤 STM32F103C8T6的ADC 输入通道 ​编辑 1路ADC&#xff08;A4 ADC 通道4&#xff09; 1路ADC源码代码链接&#xff1a; 2路ADC&#xff08;A4 ADC 通道4、A5 ADC 通道5&#xff09;基于DMA实现 多路ADC实现采集 ADC采集 ADC配置步骤 使能GPIO…...

【STM32】外部时钟|红外反射光电开关

1.外部时钟 单片机如何对外部触发进行计数&#xff1f;先看一下内部时钟&#xff0c;内部时钟是接在APB1和APB2时钟线上的&#xff0c;APB1,APB2来自stm32单片机内部的脉冲信号&#xff0c;也叫内部时钟。我们用来定时。同样我们可以把外部的信号接入单片机&#xff0c;来对其…...

【语音科学计算器】当前汇率

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配置选项之一&#xff0c;用于设置一个请求中允许的最大输入变量数。它指定了在处理POST请求或者通过URL传递的参数时&#xff0c;PHP脚本能够接收和处理的最大变量数量。 max_input_vars的默认值是1000&#xff0c;意味着一个请求中最多可以包含1000个输入…...

【云服务器】云服务器内存不够用,开启SWAP交换分区

交换分区&#xff08;Swap&#xff09; 1.创建 2GB Swap 文件 sudo fallocate -l 2G /swapfile &#xff08;如果 fallocate 不支持&#xff0c;可以用 dd 命令&#xff09; sudo dd if/dev/zero of/swapfile bs1M count2048 2.设置 Swap 权限 sudo chmod 600 /swapfile…...

未来SLAM的研究方向和热点

SLAM&#xff08;Simultaneous Localization and Mapping&#xff09;是同时定位与地图构建的缩写&#xff0c;指的是机器人或设备在一个未知环境中一边进行自我定位&#xff0c;一边构建出环境的地图。SLAM广泛应用于机器人、自动驾驶、无人机等领域&#xff0c;涉及多个研究方…...

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?

开服初期是手游最脆弱的阶段&#xff0c;极易成为DDoS攻击的目标。一旦遭遇攻击&#xff0c;可能导致服务器瘫痪、玩家流失&#xff0c;甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案&#xff0c;帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频

使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...

多场景 OkHttpClient 管理器 - Android 网络通信解决方案

下面是一个完整的 Android 实现&#xff0c;展示如何创建和管理多个 OkHttpClient 实例&#xff0c;分别用于长连接、普通 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…...

页面渲染流程与性能优化

页面渲染流程与性能优化详解&#xff08;完整版&#xff09; 一、现代浏览器渲染流程&#xff08;详细说明&#xff09; 1. 构建DOM树 浏览器接收到HTML文档后&#xff0c;会逐步解析并构建DOM&#xff08;Document Object Model&#xff09;树。具体过程如下&#xff1a; (…...

Mac软件卸载指南,简单易懂!

刚和Adobe分手&#xff0c;它却总在Library里给你写"回忆录"&#xff1f;卸载的Final Cut Pro像电子幽灵般阴魂不散&#xff1f;总是会有残留文件&#xff0c;别慌&#xff01;这份Mac软件卸载指南&#xff0c;将用最硬核的方式教你"数字分手术"&#xff0…...

EtherNet/IP转DeviceNet协议网关详解

一&#xff0c;设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络&#xff0c;本网关连接到EtherNet/IP总线中做为从站使用&#xff0c;连接到DeviceNet总线中做为从站使用。 在自动…...

零基础设计模式——行为型模式 - 责任链模式

第四部分&#xff1a;行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习&#xff01;行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想&#xff1a;使多个对象都有机会处…...

Pinocchio 库详解及其在足式机器人上的应用

Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库&#xff0c;专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性&#xff0c;并提供了一个通用的框架&…...

C++使用 new 来创建动态数组

问题&#xff1a; 不能使用变量定义数组大小 原因&#xff1a; 这是因为数组在内存中是连续存储的&#xff0c;编译器需要在编译阶段就确定数组的大小&#xff0c;以便正确地分配内存空间。如果允许使用变量来定义数组的大小&#xff0c;那么编译器就无法在编译时确定数组的大…...