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

C++11 多线程 锁与条件变量:mutex、lock_guard、unique_lock 和 condition_variable

文章目录

  • mutex
    • 核心成员函数
    • 使用场景
  • lock_guard
    • 功能和特性
    • 构造函数
    • 使用场景
  • unique_lock
    • 功能和特性
    • 构造函数
    • 核心成员函数
    • 使用场景
  • lock_guard对比unique_lock
  • condition_variable
    • 核心成员函数
    • 使用场景

mutex

std::mutex 是 C++ 标准库中提供的一种互斥量,用于在多线程环境下同步线程对共享资源的访问。通过互斥量,可以确保某一时刻只有一个线程能够访问或修改共享资源,从而避免数据竞争。

核心成员函数

  • lock():用于获取互斥量的锁。若互斥量当前未被锁定,调用线程会立即获得锁并继续执行;若互斥量已被其他线程锁定,调用线程会被阻塞,直到锁被释放并成功获取。

  • try_lock():尝试获取互斥量的锁,但不会阻塞线程。若互斥量当前未被锁定,调用线程会获得锁并返回true;若互斥量已被其他线程锁定,函数会立即返回false

  • unlock():释放互斥量的锁,允许其他线程获取该锁。调用此函数的线程必须是当前持有锁的线程,否则会导致未定义行为。

使用场景

  • 保护共享资源:当多个线程需要访问和修改同一个共享资源时,使用std::mutex可以确保在任何时刻只有一个线程能对资源进行操作,避免数据不一致的问题。例如,多个线程同时对一个全局变量进行递增操作,就需要用std::mutex保护该变量。
std::mutex mtx;
int counter = 0;void incrementCounter() {for (int i = 0; i < 1000; ++i) {// 获取互斥锁mtx.lock();// 对共享计数器进行递增++counter;// 释放互斥锁mtx.unlock();}
}

lock_guard

std::lock_guard 是 C++ 标准库中的一个 RAII(Resource Acquisition Is Initialization)锁管理器,用于简化互斥量的加锁和解锁操作。它在构造时自动加锁互斥量,在作用域结束时自动解锁,从而确保互斥量能够正确释放,避免因为异常或逻辑错误导致的死锁或资源泄漏问题。

功能和特性

  • 自动管理锁的生命周期std::lock_guard 在构造时会自动锁定互斥量,在其作用域结束时(即对象被销毁时),会自动调用互斥量的 unlock 方法解锁,无需手动调用 lockunlock,避免了因忘记解锁而导致的死锁问题。
  • 独占所有权:同一时间只有一个 std::lock_guard 对象可以拥有互斥量的所有权,确保对共享资源的独占访问,防止多线程并发访问时的数据竞争。
  • 不可复制std::lock_guard 的拷贝构造函数和拷贝赋值运算符被删除,不允许复制,因为复制可能会导致多个对象同时管理同一个互斥量,引发未定义行为。

构造函数

  • explicit lock_guard(mutex_type& m);:创建一个 std::lock_guard 对象,并立即锁定互斥量 mexplicit 关键字用于防止隐式类型转换,确保只能显式调用该构造函数。
  • lock_guard(mutex_type& m, adopt_lock_t t);:假设当前线程已经锁定了互斥量 m,创建 std::lock_guard 对象来管理该互斥量,不会再次尝试锁定。adopt_lock_t 是一个空的标记类型,用于区分不同的构造方式。

使用场景

  • 简单的临界区保护:当需要保护一个简单的代码块,确保同一时间只有一个线程可以执行该代码块时,std::lock_guard 是一个很好的选择。例如,对共享变量的读写操作、对共享数据结构的修改等。
std::mutex mtx;
int counter = 0;void incrementCounter() {for (int i = 0; i < 1000; ++i) {// 创建 std::lock_guard 对象,构造时自动锁定互斥量std::lock_guard<std::mutex> lock(mtx);// 对共享计数器进行递增++counter;// 离开此作用域,std::lock_guard 对象析构,自动解锁互斥量}
}

unique_lock

std::unique_lock 是 C++ 标准库中提供的一种线程同步工具,位于 <mutex> 头文件中。它是一个智能锁,提供了更灵活的互斥锁管理方式,与 std::lock_guard 相比,功能更强大。

功能和特性

  • 独占所有权:同一时间内,std::unique_lock保证只有一个实例能拥有互斥量,这避免了多线程环境下对共享资源的并发访问问题,确保线程安全。
  • 灵活锁定:在创建时不强制立即锁定互斥量,也可以在生命周期内随时锁定或解锁,比std::lock_guard更具灵活性。
  • 可移动性std::unique_lock支持移动语义,允许在不同作用域和函数间转移互斥量的所有权。

构造函数

  • unique_lock(mutex_type& m):创建对象并立即锁定互斥量m,若互斥量被其他线程占用,当前线程会阻塞。
  • unique_lock(mutex_type& m, std::defer_lock_t t):创建对象但不锁定互斥量,后续需手动调用lock()锁定。

核心成员函数

  • lock():锁定关联的互斥量,若被其他线程占用则阻塞。

  • unlock():解锁关联的互斥量。

  • try_lock():尝试锁定互斥量,成功返回true,失败返回false,不阻塞。

  • owns_lock():检查对象是否拥有互斥量的所有权。

  • release():释放互斥量的所有权,返回指向互斥量的指针。

  • mutex():返回管理的互斥量指针。

使用场景

  • 需要灵活锁定和解锁的场景:当在临界区中需要根据特定条件锁定或解锁互斥量时,std::unique_lock非常有用。
std::mutex mtx;
int sharedData = 0;void complexTask() {// 创建 std::unique_lock 对象,但不立即锁定互斥锁std::unique_lock<std::mutex> lock(mtx, std::defer_lock);// ...// 根据条件决定是否锁定if (true) {lock.lock();// 临界区sharedData++;std::cout << "Shared resource value: " << sharedData << std::endl;// 提前解锁lock.unlock();}// ...
}
  • 与条件变量配合使用std::condition_variablewait系列函数要求使用std::unique_lock,因为这些函数在等待期间需要解锁和重新锁定互斥量。
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>std::mutex mtx;
std::condition_variable cv;
bool ready = false;// 等待特定条件达成,继续工作
void worker() {std::unique_lock<std::mutex> lock(mtx);// 等待条件变量通知cv.wait(lock, [] { return ready; });std::cout << "Worker thread is working..." << std::endl;
}// 设置条件变量并通知等待的线程
void setReady() {{std::lock_guard<std::mutex> lock(mtx);ready = true;}// 通知等待的线程cv.notify_one();
}int main() {std::thread t1(worker);std::thread t2(setReady);t1.join();t2.join();return 0;
}

lock_guard对比unique_lock

特性std::lock_guardstd::unique_lock
自动加锁
延迟锁定
尝试锁定
手动解锁
接管已锁定的互斥量支持支持
开销较低较高
  • std::lock_guard 是 C++ 提供的简单、高效的锁管理工具,非常适合短时间的互斥量保护场景。通过 RAII 机制,std::lock_guard 大大减少了因手动管理锁而可能导致的错误。
  • std::unique_lock可以应对更复杂的锁管理需求(如延迟锁定或多次解锁/加锁)。

condition_variable

条件变量 (std::condition_variable) 是 C++ 标准库中提供的一种线程同步工具,用于线程之间的高效通信和协调。它允许一个线程等待某个条件满足时再继续执行,同时其他线程可以通知(signal)它条件已经满足。

核心成员函数

  • wait:该函数用于使当前线程阻塞,直到收到通知且满足特定条件。它有两种重载形式:
    • void wait(std::unique_lock<std::mutex>& lock);:该形式会释放当前线程持有的互斥锁lock,并使线程进入阻塞状态,直到收到其他线程的通知。当线程被唤醒时,它会重新获取互斥锁。
    • template< class Predicate > void wait(std::unique_lock<std::mutex>& lock, Predicate pred);:这种形式会在每次被唤醒时检查pred条件,如果条件不满足,则继续等待;只有当条件满足时,线程才会继续执行。
  • wait_for:与wait类似,但它允许指定一个超时时间。如果在超时时间内没有收到通知或条件未满足,线程会自动唤醒。
  • notify_one:唤醒一个等待在该条件变量上的线程。如果有多个线程在等待,只会唤醒其中一个,具体唤醒哪个线程是不确定的。
  • notify_all:唤醒所有等待在该条件变量上的线程。

使用场景

  • 生产者-消费者模型:生产者线程负责生产数据并将其放入缓冲区,消费者线程则从缓冲区取出数据进行处理。当缓冲区为空时,消费者线程需要等待;当缓冲区满时,生产者线程需要等待。
std::queue<int> buffer;
std::mutex mtx;
std::condition_variable not_full, not_empty;
const int MAX_SIZE = 5;void producer() {for (int i = 0; i < 10; ++i) {std::unique_lock<std::mutex> lock(mtx);// 等待缓冲区不满not_full.wait(lock, [] { return buffer.size() < MAX_SIZE; });buffer.push(i);std::cout << "Produced: " << i << std::endl;// 通知消费者缓冲区非空not_empty.notify_one();}
}void consumer() {for (int i = 0; i < 10; ++i) {std::unique_lock<std::mutex> lock(mtx);// 等待缓冲区非空not_empty.wait(lock, [] { return !buffer.empty(); });int item = buffer.front();buffer.pop();std::cout << "Consumed: " << item << std::endl;// 通知生产者缓冲区非满not_full.notify_one();}
}

多线程任务同步:在某些场景中,一个或多个线程需要等待其他线程完成特定任务后才能继续执行。条件变量可以用来实现这种同步机制。

线程池任务调度:在线程池中,工作线程需要等待新任务的到来。当有新任务提交时,主线程可以通知一个或多个工作线程开始执行任务。

资源分配与使用:当多个线程竞争有限的资源时,条件变量可以用于协调资源的分配和使用。例如,当资源可用时,等待的线程可以被唤醒使用资源;当资源被占用时,线程需要等待。

相关文章:

C++11 多线程 锁与条件变量:mutex、lock_guard、unique_lock 和 condition_variable

文章目录 mutex核心成员函数使用场景 lock_guard功能和特性构造函数使用场景 unique_lock功能和特性构造函数核心成员函数使用场景 lock_guard对比unique_lockcondition_variable核心成员函数使用场景 mutex std::mutex 是 C 标准库中提供的一种互斥量&#xff0c;用于在多线程…...

【Proteus】NE555纯硬件实现LED呼吸灯效果,附源文件,效果展示

本文通过NE555定时器芯片和简单的电容充放电电路,设计了一种纯硬件实现的呼吸灯方案,并借助Proteus仿真软件验证其功能。方案无需编程,成本低且易于实现,适合电子爱好者学习PWM(脉宽调制)和定时器电路原理。 一、呼吸灯原理与NE555功能分析 1. 呼吸灯核心原理 呼吸灯的…...

Cosmos - 世界模型开发平台

文章目录 一、关于 Cosmos主要特点模型家族 二、使用示例1、推理2、后训练 许可证和联系方式 一、关于 Cosmos NVIDIA Cosmos是开发者第一的世界基础模型平台&#xff0c;旨在帮助物理AI开发者更好、更快地构建他们的物理AI系统。宇宙包含 预训练模型&#xff0c;可通过拥抱脸…...

图像分割中根据mask的ROI,去除mask和image中没有勾画ROI层数以外的图像

在分割任务中&#xff0c;一个患者有很多层图像&#xff0c;但是勾画的ROI仅有那么几层。我想去除ROI以外层数的那些没用的图像。这里以一个36张图像的nii格式数据为例 查看一下mask文件中有多少个非0图像 import nibabel as nib import numpy as np# 加载 .nii 文件 file_pat…...

【Java基础-42.3】Java 基本数据类型与字符串之间的转换:深入理解数据类型的转换方法

在 Java 开发中&#xff0c;基本数据类型与字符串之间的转换是非常常见的操作。无论是从用户输入中读取数据&#xff0c;还是将数据输出到日志或界面&#xff0c;都需要进行数据类型与字符串之间的转换。本文将深入探讨 Java 中基本数据类型与字符串之间的转换方法&#xff0c;…...

全栈开发:使用.NET Core WebAPI构建前后端分离的核心技巧(一)

目录 cors解决跨域 依赖注入使用 分层服务注册 缓存方法使用 内存缓存使用 缓存过期清理 缓存存在问题 分布式的缓存 cors解决跨域 前后端分离已经成为一种越来越流行的架构模式&#xff0c;由于跨域资源共享(cors)是浏览器的一种安全机制&#xff0c;它会阻止前端应用…...

springboot使用rabbitmq

使用springboot创建rabbitMQ的链接。 整个项目结构如下&#xff1a; 1.maven依赖 <dependency><groupId>com.rabbitmq</groupId><artifactId>amqp-client</artifactId><version>3.4.1</version> </dependency>application.y…...

Linux——ext2文件系统(二)

Linux——ext2文件系统 ext2文件系统宏观认识一、磁盘分区与格式化二、块组&#xff08;Block Group&#xff09;结构三、文件系统特性 文件名与目录名与inode一、inode的作用原理二、文件与目录名与inode的关系 路径一&#xff0c;路径解析二&#xff0c;路径缓存三&#xff0…...

动手学深度学习-3.2 线性回归的从0开始

以下是代码的逐段解析及其实际作用&#xff1a; 1. 环境设置与库导入 %matplotlib inline import random import torch from d2l import torch as d2l作用&#xff1a; %matplotlib inline&#xff1a;在 Jupyter Notebook 中内嵌显示 matplotlib 图形。random&#xff1a;生成…...

“深度强化学习揭秘:掌握DQN与PPO算法的精髓“

深度Q网络&#xff08;Deep Q-Network&#xff0c;简称DQN&#xff09;是一种结合了Q学习和深度神经网络的强化学习算法。它使用神经网络来近似Q值函数&#xff0c;从而实现对复杂状态空间中的动作选择。DQN的核心思想是通过贝尔曼方程&#xff08;Bellman Equation&#xff09…...

如何让DeepSeek恢复联网功能?解决(由于技术原因,联网搜索暂不可用)

DeekSeek提示&#xff1a;&#xff08;由于技术原因&#xff0c;联网搜索暂不可用&#xff09; 众所周知&#xff0c;因为海外黑客的ddos攻击、僵尸网络攻击&#xff0c;deepseek的联网功能一直处于宕机阶段&#xff0c;但是很多问题不联网出来的结果都还是2023年的&#xff0c…...

Unity-编译构建Android的问题记录

文章目录 报错&#xff1a;AAPT2 aapt2-4.1.2-6503028-osx Daemon #0 Failed to shutdown within timeout报错信息解读&#xff1a;原因分析最终处理方法 报错&#xff1a;AAPT2 aapt2-4.1.2-6503028-osx Daemon #0 Failed to shutdown within timeout 报错信息解读&#xff1…...

python的ruff简单使用

Ruff 是一个用 Rust 编写的高性能 Python 静态分析工具和代码格式化工具。它旨在提供快速的代码检查和格式化功能&#xff0c;同时支持丰富的配置选项和与现有工具的兼容性。ruff是用rust实现的python Linter&Formatter。 安装&#xff1a; conda install -c conda-forge…...

Docker 部署 GLPI(IT 资产管理软件系统)

GLPI 简介 GLPI open source tool to manage Helpdesk and IT assets GLPI stands for Gestionnaire Libre de Parc Informatique&#xff08;法语 资讯设备自由软件 的缩写&#xff09; is a Free Asset and IT Management Software package, that provides ITIL Service De…...

【漫话机器学习系列】077.范数惩罚是如何起作用的(How Norm Penalties Work)

范数惩罚的作用与原理 范数惩罚&#xff08;Norm Penalty&#xff09; 是一种常用于机器学习模型中的正则化技术&#xff0c;它的主要目的是控制模型复杂度&#xff0c;防止过拟合。通过对模型的参数进行惩罚&#xff08;即在损失函数中加入惩罚项&#xff09;&#xff0c;使得…...

【C++ STL】vector容器详解:从入门到精通

【C STL】vector容器详解&#xff1a;从入门到精通 摘要&#xff1a;本文深入讲解C STL中vector容器的使用方法&#xff0c;涵盖常用函数、代码示例及注意事项&#xff0c;助你快速掌握动态数组的核心操作&#xff01; 一、vector概述 vector是C标准模板库&#xff08;STL&am…...

LLMs之OpenAI o系列:OpenAI o3-mini的简介、安装和使用方法、案例应用之详细攻略

LLMs之OpenAI o系列&#xff1a;OpenAI o3-mini的简介、安装和使用方法、案例应用之详细攻略 目录 相关文章 LLMs之o3&#xff1a;《Deliberative Alignment: Reasoning Enables Safer Language Models》翻译与解读 LLMs之OpenAI o系列&#xff1a;OpenAI o3-mini的简介、安…...

Notepad++消除生成bak文件

设置(T) ⇒ 首选项... ⇒ 备份 ⇒ 勾选 "禁用" 勾选禁用 就不会再生成bak文件了 notepad怎么修改字符集编码格式为gbk 如图所示...

后台管理系统通用页面抽离=>高阶组件+配置文件+hooks

目录结构 配置文件和通用页面组件 content.config.ts const contentConfig {pageName: "role",header: {title: "角色列表",btnText: "新建角色"},propsList: [{ type: "selection", label: "选择", width: "80px&q…...

Spring Boot项目如何使用MyBatis实现分页查询

写在前面&#xff1a;大家好&#xff01;我是晴空๓。如果博客中有不足或者的错误的地方欢迎在评论区或者私信我指正&#xff0c;感谢大家的不吝赐教。我的唯一博客更新地址是&#xff1a;https://ac-fun.blog.csdn.net/。非常感谢大家的支持。一起加油&#xff0c;冲鸭&#x…...

[Java]多态

1. 多态的基本概念 1.1 定义&#xff1a; 多态是指同一操作作用于不同的对象时&#xff0c;能够表现出不同的行为。多态通常通过以下两种方式实现&#xff1a; 方法重载&#xff08;Overloading&#xff09;方法重写&#xff08;Overriding&#xff09; 1.2 示例&#xff1…...

用Impala对存储在HDFS中的大规模数据集进行快速、实时的交互式SQL查询的具体步骤和关键代码

AWS EMR&#xff08;Elastic MapReduce&#xff09;中应用Impala的典型案例&#xff0c;主要体现在大型企业和数据密集型组织如何利用Impala对存储在Hadoop分布式文件系统&#xff08;HDFS&#xff09;中的大规模数据集进行快速、实时的交互式SQL查询。以下是一个具体的案例说明…...

Intellij 插件开发-快速开始

目录 一、开发环境搭建以及创建action1. 安装 Plugin DevKit 插件2. 新建idea插件项目3. 创建 Action4. 向新的 Action 表单注册 Action5. Enabling Internal Mode 二、插件实战开发[不推荐]UI Designer 基础JBPanel类&#xff08;JPanel面板&#xff09;需求&#xff1a;插件设…...

GIt使用笔记大全

Git 使用笔记大全 1. 安装 Git 在终端或命令提示符中&#xff0c;输入以下命令检查是否已安装 Git&#xff1a; git --version如果未安装&#xff0c;可以从 Git 官方网站 下载并安装适合你操作系统的版本。 2. 配置 Git 首次使用 Git 时&#xff0c;需要配置用户名和邮箱…...

语言月赛 202412【题目名没活了】题解(AC)

》》》点我查看「视频」详解》》》 [语言月赛 202412] 题目名没活了 题目描述 在 XCPC 竞赛里&#xff0c;会有若干道题目&#xff0c;一支队伍可以对每道题目提交若干次。我们称一支队伍对一道题目的一次提交是有效的&#xff0c;当且仅当&#xff1a; 在本次提交以前&…...

MySQL锁类型(详解)

锁的分类图&#xff0c;如下&#xff1a; 锁操作类型划分 读锁 : 也称为共享锁 、英文用S表示。针对同一份数据&#xff0c;多个事务的读操作可以同时进行而不会互相影响&#xff0c;相互不阻塞的。 写锁 : 也称为排他锁 、英文用X表示。当前写操作没有完成前&#xff0c;它会…...

面经--C语言——static,volatile,malloc,使用异或进行数据交换

文章目录 static静态变量和全局变量的区别volatile主要作用 malloc1. 内存分配器的作用2. 内存分配过程(1) 查找空闲内存块(2) 扩展堆空间(3) 元数据 3. 内存释放过程(1) 标记为可用(2) 合并相邻空闲块(3) 延迟释放 4. 内存管理策略(1) 分配缓存&#xff08;Allocation Caching…...

stm32小白成长为高手的学习步骤和方法

我们假定大家已经对STM32的书籍或者文档有一定的理解。如不理解&#xff0c;请立即阅读STM32的文档&#xff0c;以获取最基本的知识点。STM32单片机自学教程 这篇博文也是一篇不错的入门教程&#xff0c;初学者可以看看&#xff0c;讲的真心不错。 英文好的同学&#xf…...

OSCP - Proving Grounds - Roquefort

主要知识点 githook 注入Linux path覆盖 具体步骤 依旧是nmap扫描开始&#xff0c;3000端口不是很熟悉&#xff0c;先看一下 Nmap scan report for 192.168.54.67 Host is up (0.00083s latency). Not shown: 65530 filtered tcp ports (no-response) PORT STATE SERV…...

集合通讯概览

&#xff08;1&#xff09;通信的算法 是根据通讯的链路组成的 &#xff08;2&#xff09;因为通信链路 跟硬件强相关&#xff0c;所以每个CCL的库都不一样 芯片与芯片、不同U之间是怎么通信的&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 很重要…...