【C++并发编程】(三)互斥锁:std::mutex

文章目录

  • 互斥锁
    • 数据竞争
    • C++互斥锁
      • lock() 和 unlock()
      • std::lock_guard

互斥锁

数据竞争

在并发编程中,数据竞争是指多个线程同时对共享数据进行读写操作,并且至少有一个线程进行写操作,从而导致未定义的行为或结果。
下面给出一个例子:

#include <iostream>  
#include <thread>  int counter = 0; // 共享数据void increment() {  for (int i = 0; i < 100000; ++i) {  ++counter; // 修改被共享的数据}  
}  int main() {  std::thread th1(increment);  std::thread th2(increment);  th1.join();  th2.join();  // 预期输出是200000,但由于数据竞争,实际输出可能小于这个值  std::cout << "Final counter value: " << counter << std::endl;  // Final counter value: 107194return 0;  
}

两个线程t1t2可能同时对counter进行自增操作,导致counter的值增加的数量少于预期(即少于200000)。这是因为:

自增操作包含三个步骤:

  • 从内存中读取counter的当前值0。
  • 将这个值加1。
  • 将结果1写回内存中的counter。

如果两个线程同时执行这些步骤,可能会发生以下情况:

  • 线程t1读取counter的值为0。
  • 线程t2也读取counter的值为0(因为线程t1还没有将新的值写回内存)。
  • 线程t1将1写回counter。
  • 线程t2也将1写回counter(因为它之前读取的值是0)。

C++互斥锁

互斥锁(互斥量)能保护多个线程的共享资源不被同时访问。互斥锁的状态只有两种:开锁(unlocked)和闭锁(locked)。当一个任务(或线程)持有互斥锁,且该互斥锁处于闭锁状态,那么这个任务可以执行访问共享资源的代码,其他任务(或线程)则不被允许。开锁后,该线程将失去了对互斥锁的所有权,其他正在等待的线程现在有机会获取这个互斥锁,以访问共享资源。

在C++11中提供了std::mutex支持这一功能。std::mutex是一个简单的互斥锁类,它有两个主要操作:lock()unlock(),用于实现闭锁和开锁。

**lock():**当一个线程调用 lock() 函数时,它会尝试获取与该函数关联的互斥锁。如果互斥量当前没有被其他线程锁定(即它是可用的),则调用线程会成功获取锁,并继续执行其后续代码。
如果互斥量已经被其他线程锁定,则调用线程会被阻塞(即它会停止执行,直到锁变得可用)。
一旦线程获取了锁,它就可以安全地访问共享资源,而不必担心其他线程同时修改它。

unlock(): 当一个线程完成对共享资源的访问后,它应该调用 unlock() 函数来释放与该函数关联的互斥量(或其他锁)。释放锁允许其他线程获取该锁并访问共享资源。

在C++中,直接使用 lock()unlock() 函数来管理锁可能会导致一些问题,特别是当代码路径变得复杂时。为了简化锁的管理并减少出错的可能性,C++11引入了 std::lock_guardstd::unique_lock 等RAII(Resource Acquisition Is Initialization)风格的包装器,用于自动管理互斥锁的开锁和闭锁。其中,std::lock_guard 是最简单的锁包装器,它只提供了基本的锁定和解锁功能。 std::unique_lock 提供了比 std::lock_guard 更多的功能,更灵活,后续再单独介绍。

下面给出示例展示如何使用lock()unlock()以及锁包装器std::lock_guard 进行互斥锁的闭锁和开锁。

lock() 和 unlock()

#include <iostream>  
#include <thread>  
#include <mutex>  std::mutex mtx; // 创建一个互斥锁实例,用于保护共享数据
int counter = 0; // 共享数据 void increment() {  for (int i = 0; i < 100000; ++i) {  // 显式地调用了lock()和unlock()来分别获取和释放锁:mtx.lock(); // 获取锁  ++counter; // 递增操作  mtx.unlock(); // 释放锁  }  
}  int main() {  std::thread th1(increment);  std::thread th2(increment);  th1.join();  th2.join();  std::cout << "Final counter value: " << counter << std::endl;  // Final counter value: 200000return 0;  
}

std::lock_guard

#include <iostream>  
#include <thread>  
#include <mutex>  std::mutex mtx; // 互斥锁用于保护共享数据
int counter = 0; // 共享数据 void increment() {  std::unique_lock<std::mutex> lock(mtx); // 构造时自动获取锁  for (int i = 0; i < 100000; ++i) {  ++counter; // 递增操作,因为lock存在,所以此操作是安全的  }  // lock在离开作用域时自动释放锁  
}  int main() {  std::thread th1(increment);  std::thread th2(increment);  th1.join();  th2.join();  std::cout << "Final counter value: " << counter << std::endl;  // Final counter value: 200000return 0;  
}

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

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

相关文章

DETR类型检测网络实验2---优化测试

补全reference_point Anchor-DETR提出用预定义的参考点生成query_pos; DBA-DETR提出预定义参考信息由(x,y)增至(x,y,w,h) 那么在3D检测任务中是否可以把预定义参考信息补全为(x,y,z,l,w,h,sint,cost),而query_pos都是使用xy两个维度(因为是bev网络). (这种方法在Sparse-DETR中…

轻松应对数据恢复挑战:雷神笔记本,不同情况不同策略

在数字化时代&#xff0c;数据无疑是我们生活中不可或缺的一部分。无论是重要的工作文件、珍贵的家庭照片&#xff0c;还是回忆满满的视频&#xff0c;一旦丢失&#xff0c;都可能给我们的生活带来诸多不便。雷神笔记本作为市场上备受欢迎的电脑品牌&#xff0c;用户在使用过程…

计算机网络实验二:交换机的基本配置与操作

实验二:交换机的基本配置与操作 一、实验要求 (1)掌握windows网络参数的设置(TCP/IP协议的设置); (2)掌握交换机命令行各种操作模式的区别,以及模式之间的切换; (3)掌握交换机的全局的基本配置; (4)掌握交换机端口的常用配置参数; (5)查看交换机系统和…

Adobe-Premiere-CEP 扩展 入门-视频剪辑-去气口插件-Silence Remover

短视频&#xff0c;这两年比较火&#xff0c;不要再问为什么用Premiere&#xff0c;非常难用&#xff0c;为什么不用某影&#xff0c;某些国内软件非常接地气简单&#xff0c;又例如某音资深的视频短编辑就很好用了。。。 Premiere二次开发调试难&#xff0c;不如自己搞个cons…

perl:用 MIDI::Simple 生成midi文件,用 pygame 播放 mid文件

在 csdn.net 下载 strawberry-perl-5.32.1.1-64bit.zip 解压安装在 D:\Strawberry\ 运行 cpan install MIDI::Simple D:\Strawberry\c\bin\gmake.exe test -- OK Running make install for CONKLIN/MIDI-Perl-0.84.tar.gz Installing D:\Strawberry\perl\site\lib\MIDI.pm I…

libcity 笔记:支持的数据集

1 支持的数据集&#xff08;model&#xff09; 1.1 traffic_state_pred HA历史平均值&#xff0c;将历史流量建模为季节性过程&#xff0c;然后使用前几个季节的加权平均值作为预测值。VAR向量自回归SVR支持向量回归ARIMAAutoEncoderSeq2Seq采用基于门控循环单元的编码器-解码…

kubebuilder(6)webhook

operator中的webhook也是很重要的一块功能。也是相对比较独立的模块&#xff0c;所以放在后面讲。 webhook是一个callback&#xff0c;注册到k8s的api-server上。当某个特定的时间发生时&#xff0c;api server就会查询注册的webhook&#xff0c;并根据一些逻辑确认转发消息给…

【ARM Cortex-M3指南】3:Cortex-M3基础

文章目录 三、Cortex-M3基础3.1 寄存器3.1.1 通用目的寄存器 R0~R73.1.2 通用目的寄存器 R8~R123.1.3 栈指针 R133.1.4 链接寄存器 R143.1.5 程序计数器 R15 3.2 特殊寄存器3.2.1 程序状态寄存器3.2.2 PRIMASK、FAULTMASK和BASEPRI寄存器3.2.3 控制寄存器 3.3 操作模式3.4 异常…

KNN算法详解

KNN分类算法 K最近邻&#xff08;K-Nearest Neighbors&#xff0c;KNN&#xff09;算法是一种简单且直观的监督学习算法&#xff0c;用于分类和回归问题。在KNN分类中&#xff0c;通过将新数据点的特征与训练数据中的所有点进行比较&#xff0c;确定其最近邻居&#xff0c;并将…

经典后台管理UI控制台

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>西门星空乐园管理后台</title><script src"../vue_js/vue.js"></script><style>body {margin: 0;padding: 0;width…

MRI学习笔记-spm独立样本t检验

SPM操作&#xff1a; 9-SPM-fmri任务态 二阶分析&#xff1a; 双样本t检验 - 知乎 (zhihu.com) 批处理脚本&#xff1a; clc; clear; nmodel 3; numsub 27; rootdir D:\LLYdata\motor_inhibition_data2\motor_inhibition_fmri;%数据最上层路径 ffxname data_05_1st_leve…

使用FPGA实现串-并型乘法器

介绍 其实我们知道&#xff0c;用FPGA实现乘法器并不是一件很简单的事&#xff0c;而且在FPGA中也有乘法器的IP核可以直接调用&#xff0c;我这里完全就是为了熟悉一些FPGA的语法然后写了这样一个电路。 串-并型乘法器模块 从字面上看&#xff0c;串-并乘法器就是其中一个乘数…

Java面试问题及答案

Java面试问题及答案 以下是几个Java面试中可能会问到的问题及其答案。 1. 解释Java中的多态性是什么&#xff0c;以及它是如何工作的&#xff1f; 问题&#xff1a; 在Java中&#xff0c;多态性是指允许不同类的对象对同一消息做出响应的能力&#xff0c;即同一个接口可以被…

FastApi个人笔记

FastApi笔记 学习FastApi建议直接看官方文档:官方文档 文章目录 FastApi笔记一、环境准备1.1 安装Fast Api 二、第一个Restful API2.1 创建第一个代码框架async作用[(引自CSDN)](https://blog.csdn.net/The_Time_Runner/article/details/105646363): 2.2 运行第一个代码2.3 Swa…

Nodejs process.nextTick() 使用详解

还是大剑师兰特&#xff1a;曾是美国某知名大学计算机专业研究生&#xff0c;现为航空航海领域高级前端工程师&#xff1b;CSDN知名博主&#xff0c;GIS领域优质创作者&#xff0c;深耕openlayers、leaflet、mapbox、cesium&#xff0c;canvas&#xff0c;webgl&#xff0c;ech…

OpenCV 为轮廓创建边界框和圆(62)

返回:OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇:OpenCV检测凸包(61) 下一篇 :OpenCV如何为等值线创建边界旋转框和椭圆(62) ​ 目标 在本教程中&#xff0c;您将学习如何&#xff1a; 使用 OpenCV 函数 cv::boundingRect使用 OpenCV 函数 cv::mi…

数据库(MySQL)—— 事务

数据库&#xff08;MySQL&#xff09;—— 事务 什么是事务事务操作未控制事务测试异常情况 控制事务一查看/设置事务提交方式&#xff1a;提交事务回滚事务 控制事务二开启事务提交事务回滚事务 并发事务问题脏读&#xff08;Dirty Read&#xff09;不可重复读&#xff08;Non…

python在Django中,模板加载器(Template Loaders)是负责查找和加载模板文件的组件

在Django中&#xff0c;模板加载器&#xff08;Template Loaders&#xff09;是负责查找和加载模板文件的组件。Django默认使用django.template.loaders.filesystem.Loader和django.template.loaders.app_directories.Loader这两个加载器&#xff0c;它们分别用于从文件系统和D…

数据结构练习题---环形链表详解

链表成环&#xff0c;在力扣中有这样的两道题目 https://leetcode.cn/problems/linked-list-cycle/ https://leetcode.cn/problems/linked-list-cycle-ii/description/ 这道题的经典解法是利用快慢指针&#xff0c;如果链表是一个环形链表&#xff0c;那么快指针(fast)和慢指…

关于MS-DOS时代的回忆

目录 一、MS-DOS是什么&#xff1f; 二、MS-DOS的主要功能有哪些&#xff1f; 三、MS-DOS的怎么运行的&#xff1f; 四、微软开源MS-DOS源代码 五、高手与漂亮女同学 一、MS-DOS是什么&#xff1f; MS-DOS&#xff08;Microsoft Disk Operating System&#xff09;是微软公…