C++ 中的 std::mutex 与多线程同步

在多线程编程中,互斥锁(Mutex) 是一种同步机制,用于保护共享资源(如变量、数据结构)免受数据竞争(Data Race)的影响。C++ 标准库中的 std::mutex 提供了基本的互斥功能,常与 std::lock_guardstd::unique_lock 配合使用,以实现线程安全的资源访问。


1. std::mutex 的基本概念

  • 作用:确保在同一时刻只有一个线程可以访问共享资源。
  • 头文件#include <mutex>
  • 常用方法
    • lock():锁定互斥锁,若已被锁定则阻塞等待。
    • unlock():释放互斥锁。
    • try_lock():尝试锁定互斥锁,若失败则立即返回(不阻塞)。

2. 示例:不使用互斥锁导致数据竞争

场景:多个线程对共享计数器进行递增操作。
#include <iostream>
#include <thread>
#include <vector>int counter = 0;void increment() {for (int i = 0; i < 100000; ++i) {++counter;  // 非原子操作,存在数据竞争}
}int main() {std::vector<std::thread> threads;for (int i = 0; i < 10; ++i) {threads.emplace_back(increment);}for (auto& t : threads) {t.join();}std::cout << "Final counter value: " << counter << std::endl;return 0;
}

输出(每次运行结果可能不同):

Final counter value: 789123  // 错误值(期望 1,000,000)

问题分析

  • ++counter 不是原子操作,多个线程同时修改 counter 导致数据竞争。
  • 结果不可预测,可能远小于预期值。

3. 使用 std::mutexstd::lock_guard 修复数据竞争

改进方案:通过互斥锁保护共享资源。
#include <iostream>
#include <thread>
#include <vector>
#include <mutex>  // 引入 mutexint counter = 0;
std::mutex mtx;  // 定义互斥锁void increment() {for (int i = 0; i < 100000; ++i) {mtx.lock();     // 加锁++counter;      // 安全访问共享资源mtx.unlock();   // 解锁}
}int main() {std::vector<std::thread> threads;for (int i = 0; i < 10; ++i) {threads.emplace_back(increment);}for (auto& t : threads) {t.join();}std::cout << "Final counter value: " << counter << std::endl;return 0;
}

输出

Final counter value: 1000000

关键点

  • 互斥锁保护临界区mtx.lock()mtx.unlock() 之间的代码为临界区,确保一次只有一个线程执行。
  • 避免死锁:必须成对调用 lock()unlock(),否则可能导致死锁。

4. 使用 std::lock_guard 自动管理锁的生命周期

改进方案:使用 RAII(Resource Acquisition Is Initialization) 模式自动加锁/解锁。
#include <iostream>
#include <thread>
#include <vector>
#include <mutex>int counter = 0;
std::mutex mtx;void increment() {for (int i = 0; i < 100000; ++i) {std::lock_guard<std::mutex> lock(mtx);  // 构造时加锁,析构时自动解锁++counter;  // 安全访问共享资源}
}int main() {std::vector<std::thread> threads;for (int i = 0; i < 10; ++i) {threads.emplace_back(increment);}for (auto& t : threads) {t.join();}std::cout << "Final counter value: " << counter << std::endl;return 0;
}

输出

Final counter value: 1000000

优势

  • 自动解锁lock_guard 在作用域结束时自动调用 unlock(),避免手动管理锁。
  • 异常安全:即使发生异常,lock_guard 也会确保解锁。

5. std::unique_lock:更灵活的锁管理

使用场景:需要延迟锁定、条件变量或动态锁定策略。
#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx;
int shared_data = 0;void access_data() {std::unique_lock<std::mutex> lock(mtx, std::defer_lock); // 不立即加锁// 做一些不需要锁的操作...lock.lock();  // 显式加锁shared_data = 42;std::cout << "Data updated: " << shared_data << std::endl;// lock 在作用域结束时自动解锁
}int main() {std::thread t(access_data);t.join();return 0;
}

输出

Data updated: 42

特点

  • 延迟锁定:使用 std::defer_lock 参数延迟加锁。
  • 支持移动:可以将锁的所有权转移到其他线程。
  • 配合条件变量:常用于 std::condition_variable 的等待操作。

6. 互斥锁的最佳实践

原则说明
保护共享资源所有对共享资源的访问都必须通过互斥锁保护。
最小化临界区锁定时间越短越好,避免阻塞其他线程。
避免死锁按固定顺序加锁,不嵌套加锁。
使用 RAII 风格优先使用 lock_guardunique_lock,避免手动调用 lock()/unlock()
避免递归加锁默认 std::mutex 不支持递归加锁,多次调用 lock() 会导致死锁。

7. 总结

  • 互斥锁(std::mutex 是 C++ 多线程编程中保护共享资源的核心工具。
  • std::lock_guard 提供自动加锁/解锁,适合大多数同步场景。
  • std::unique_lock 提供更灵活的锁管理,适用于复杂同步需求(如条件变量)。
  • 关键点:确保所有共享资源访问都通过互斥锁保护,避免数据竞争和死锁。

通过合理使用互斥锁,可以编写出高效、安全的多线程程序。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/87038.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/87038.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/87038.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络安全2023—新安全新发展

关于绿盟科技 绿盟科技集团股份有限公司(以下简称绿盟科技),成立于 2000 年 4 月,总部位于北京。公司于 2014 年 1 月 29 日在深圳证券交易所创业板上市,证券代码:300369。绿盟科技在国内设有 50余个分支机构,为政府、金融、运营商、能源、交通、科教文卫等行业用户与各…

WebSocket扫盲

WebSocket 是一种网络通信协议&#xff0c;它允许在单个 TCP 连接上进行全双工、双向的实时通信。它是为了解决传统 HTTP 协议在实时交互应用中的局限性而设计的。 核心概念和特点 解决 HTTP 的痛点&#xff1a; 单向性&#xff1a; HTTP 是请求-响应模式。客户端发起请求&…

Springboot整合高德地图

1.登录高德开放平台 高德开放平台 | 高德地图API 2.获取密钥key 1.点击控制台 2.创建新应用 3.添加key 4.创建key 5.获取key 3.java整合 1.高德配置类 package com.thk.controller.map;import org.springframework.beans.factory.annotation.Value; import org.springfram…

【SQL知识】PDO 和 MySQLi 的区别

目录 简介 主要区别 预处理语句示例比较 PDO 示例 MySQLi 示例 选择建议 简介 PDO (PHP Data Objects) 和 MySQLi (MySQL Improved) 都是 PHP 中用于数据库操作的扩展&#xff0c;都支持预处理语句&#xff0c;但有一些重要区别&#xff1a; 主要区别 数据库支持 PDO&am…

python打卡 DAY 45 Tensorboard使用介绍

目录 一、TensorBoard 发展历史与原理 1. 演进历程 2. 核心架构原理 二、TensorBoard 核心功能操作 1. 基础配置方法 2. 常用功能速查表 三、CIFAR10 实战演示 1. MLP 模型监控配置 2. CNN 特征可视化 四、TensorBoard 高级功能 1. 超参数调优 2. 3D点云可视化 五、…

Swift 中 Result 类型全解析:从基础到进阶

在现代 iOS 开发中&#xff0c;Swift 的 Result 类型是处理同步与异步错误的一大利器。相比传统的 throws / do-catch 语法&#xff0c;它更清晰、结构化&#xff0c;也更易于组合式编程。 本文将带你从 Result 的基础定义出发&#xff0c;逐步深入其在实际项目中的多种应用&am…

Github 2025-06-28 Rust开源项目日报 Top10

根据Github Trendings的统计,今日(2025-06-28统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Rust项目10Rust实现的非官方Bitwarden兼容服务器 创建周期:2317 天开发语言:Rust协议类型:GNU Affero General Public License v3.0Star数量…

python 写一个判断文本中是否有手机号的函数,并提取出文本中的手机号

我们需要判断文本中是否有手机号&#xff0c;并提取出手机号。 中国大陆的手机号规则&#xff1a; 1. 通常为11位数字。 2. 目前手机号段分配如下&#xff1a; - 移动号段&#xff1a;134(0-8)、135、136、137、138、139、147、148、150、151、152、157、158、159、172、178、1…

作物生长模型Oryza V3实战12:drate程序详解

drate(v2).exe,可以通过观察移植日、穗部分化、开花和成熟的物候日期(即日和年),DRATE(v2)用于校准四个阶段的发展速率:幼苗期(DVRJ,oCday-1)、光周期敏感期(DVRI,oCday-1)、穗部发育期(DVRP,oCday-1)和生殖期(DVRR,oCday-1)。 一 准备输入文件 1、准备.crp,.…

利用视觉-语言模型搭建机器人灵巧操作的支架

25年6月来自斯坦福和德国卡尔斯鲁厄理工的论文“Scaffolding Dexterous Manipulation with Vision-Language Models”。 灵巧机械手对于执行复杂的操作任务至关重要&#xff0c;但由于演示收集和高维控制的挑战&#xff0c;其训练仍然困难重重。虽然强化学习 (RL) 可以通过在模…

面试拷打-20250701

memcopy和memmov 详细解释 示例1&#xff1a;不重叠的内存区域 正常复制。 示例2&#xff1a;重叠的内存区域 原始数据&#xff1a;src2是一个包含字符串"HelloWorld"的字符数组。使用memcpy&#xff1a; memcpy(src2 2, src2, 5);试图将src2中的前5个字符复制…

什么是 BigKey?

Redis BigKey 深度解析&#xff1a;识别、危害与优化方案 什么是 BigKey&#xff1f; 在 Redis 中&#xff0c;BigKey 是指存储大量数据的单个键&#xff0c;这些键通常具有异常大的内存占用或包含大量元素。BigKey 不是由数据类型定义&#xff0c;而是由其资源消耗决定的。 …

量化选股策略 聚宽

# 量化选股策略完整分析与优化建议 ## 策略整体架构分析 这个量化交易策略主要由以下几个核心部分组成&#xff1a; 1. **初始化设置**&#xff1a;配置基准指数、交易参数和全局变量 2. **选股逻辑**&#xff1a;通过财务指标筛选优质股票 3. **股票过滤**&#xff1a;排除…

Python 数据分析:numpy,抽提,布尔索引2。

目录 1 示例代码2 欢迎纠错3 论文写作/Python 学习智能体------以下关于 Markdown 编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右S…

解决leetcode第3597题分割字符串

3597. 分割字符串 难度&#xff1a;中等 问题描述&#xff1a; 给你一个字符串 s&#xff0c;按照以下步骤将其分割为 互不相同的段 &#xff1a; 从下标 0 开始构建一个段。 逐字符扩展当前段&#xff0c;直到该段之前未曾出现过。 只要当前段是唯一的&#xff0c;就将其…

电源芯片之DCDC初探索ING

1. 概述 DC-DC转换器的意思是直流变直流&#xff08;不同的直流电源值得转换&#xff09;&#xff0c;是一种在直流电路中将一个电压值的电能变为另一个电压值的电能装置。 DC-DC转换器一般由控制芯片、电感线圈、二极管、三极管、电容器构成。 2. 基本拓扑结构 2.1 非隔离…

JavaEE:分布式session

一、使用Redis存储分布式session&#xff1a; 1.SpringBoot整合Redis&#xff0c;见如下地址&#xff1a; JavaEE&#xff1a;SpringBoot整合Redis_a526001650a-CSDN博客 2.代码实现分布式session存储(此处以token为例)&#xff1a; Autowired private RedisTemplate<St…

OpenCV CUDA模块设备层-----“大于阈值设为零” 的图像处理函数 thresh_to_zero_inv_func()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 OpenCV 的 CUDA 模块&#xff08;cudev&#xff09; 中的一个仿函数生成器&#xff0c;用于创建一个 “大于阈值设为零” 的图像处理函数对象。 …

FastGPT与MCP:解锁AI新时代的技术密码

一、AI 浪潮中的新星&#xff1a;FastGPT 与 MCP 登场 在当今科技飞速发展的时代&#xff0c;人工智能&#xff08;AI&#xff09;已成为推动各行业变革的核心力量。从智能语音助手到复杂的图像识别系统&#xff0c;AI 的应用无处不在&#xff0c;而其中的关键技术 —— 语言模…

browser-tools-mcp + excel-mcp-server + cursor 实现读取网页信息自动写入Excel

browser-tools-mcp excel-mcp-server cursor 实现读取网页信息自动写入Excel 文章目录 browser-tools-mcp excel-mcp-server cursor 实现读取网页信息自动写入Excel一、安装node.js和npm1、安装nvm2、安装最新版本的node.js 二、安装browser-tools-mcp1、安装 BrowserTools…