【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: 107194
    
    return 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: 200000
    
  
    return 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: 200000
  
    return 0;  
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Leetcode每日一题】 综合练习 - 全排列 II(难度⭐⭐)(71)

1. 题目解析 题目链接&#xff1a;47. 全排列 II 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 2.算法原理 算法思路梳理 为了生成给定数组nums的全排列&#xff0c;同时避免由于重复元素导致的重复排列&#xff0c;我们可以遵…

刷代码随想录有感(56):二叉搜索树的最小绝对差

题干&#xff1a; 代码:中序遍历成有序数组逐一比较相邻两个数之间的差值&#xff0c;注意这里是取最小值所以定义的初始值应该是非常大的INT_MAX&#xff01;&#xff01;&#xff01; class Solution { public:void traversal(TreeNode* root, vector<int>&a){if(…

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

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

c++多线程2小时速成

简介 c多线程基础需要掌握这三个标准库的使用&#xff1a;std::thread,std::mutex, andstd::async。 1. Hello, world #include <iostream> #include <thread>void hello() { std::cout << "Hello Concurrent World!\n"; }int main() {std::th…

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

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

ubuntu使用Remmina远程连接Windows桌面

概况 目的&#xff1a; 远程连接公司电脑写一点代码 之前的方案&#xff1a; 安装Win10虚拟机&#xff0c;虚拟机里连接 VPN&#xff0c; 然后用 mstsc 命令连接。 新的方案&#xff1a;连接VPN后&#xff0c; 开启Remmina直接连接远程 Windows 桌面 新方案优点&#xff1a…

分布式锁之-mysql

使用mysql实现分布式锁的方式这里演示两种&#xff1a; 1:基于 MySQL 实现的乐观锁 2:基于 MySQL 实现的悲观锁 数据库脚本 SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS 0;-- ---------------------------- -- Table structure for product_stock -- -----------------------…

【Python】机器学习之Sklearn基础教程大纲

机器学习之Sklearn基础教程大纲 1. 引言 机器学习简介Scikit-learn&#xff08;Sklearn&#xff09;库介绍安装和配置Sklearn 2. 数据预处理 2.1 数据加载与查看 - 加载CSV、Excel等格式的数据- 查看数据的基本信息&#xff08;如形状、数据类型等&#xff09;2.2 数据清洗…

Vue 组件间的数据绑定

在Vue组件中&#xff0c;v-model指令可以用来实现双向数据绑定。它用于将组件的属性和父组件中的数据进行双向绑定&#xff0c;使得当属性的值改变时&#xff0c;父组件中的数据也会相应地改变&#xff0c;并且当父组件中的数据改变时&#xff0c;属性的值也会相应地改变。 目…

【软考高项】三十一、成本管理4个过程

一、规划成本管理 1、定义、作用 定义&#xff1a;确定如何估算、预算、管理、监督和控制项目成本的过程作用&#xff1a;在整个项目期间为如何管理项目成本提供指南和方向 应该在项目规划阶段的早期就对成本管理工作进行规划&#xff0c;建立各成本管理过程的基本框架&…

使用docker-compose编排lnmp(dockerfile) 完成Wordpress

实验环境&#xff1a; 在已有docker环境和nginx镜像的基础上进行编排。 1、准备mysql容器目录及文件 2、dockerfile文件内容 3、my.cnf文件内容 4、准备php容器目录及文件 5、dockerfile文件内容 6、准备其他文件 7、编写docker-compose.yml文件 8、Docker Compose环境的实现…

Redisson 分布式锁和同步器

系列文章目录 文章目录 系列文章目录前言前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 redisson 是基于redis的扩展库,使得redis除了应用于缓存以外,还能做队列…

AI学习指南-人工智能概述

欢迎来到人工智能的奇妙世界&#xff01;如果你是初学者&#xff0c;那么你来对地方了。今天&#xff0c;我们将一起探索人工智能&#xff08;AI&#xff09;的基本概念&#xff0c;看看它是如何分类的&#xff0c;它的应用有哪些&#xff0c;以及未来可能的发展方向。准备好了…

Django之单文件上传(以图片为例)

一&#xff0c;创建项目 初始化&#xff0c;数据迁移&#xff0c;创建superuser&#xff0c;创建app等 二&#xff0c;配置settings.py 1&#xff0c;配置数据库&#xff08;本作者使用的mysql&#xff09;&#xff0c;以前文章有提到 2&#xff0c;配置静态文件存放路径 STAT…

Spring Cloud:探索它的核心组件,揭秘微服务生态

Spring Cloud简介 在我们的编程旅程中&#xff0c;我们会遇到各种各样的工具和技术&#xff0c;它们如同繁星般点缀在编程的天空中&#xff0c;而Spring Cloud就是其中一颗明亮的星。那么&#xff0c;什么是Spring Cloud呢&#xff1f; Spring Cloud&#xff0c;是一个基于Spr…

《尿不湿级》STM32 F103C8T6最小系统板搭建(五)BOOT

一、BOOT是什么&#xff1f; 大多数初学者第一次接触BOOT总是对这个词感到不解&#xff0c;从哪冒出一个奇奇怪怪的东西还要接跳线帽&#xff0c;为什么要配置它才能进行串口程序的下载&#xff1f;为什么不正确配置会导致单片机无法正常启动…… boot&#xff0c;及物动词&…

IOS 开发 - block 使用详解

1.Blobk的定义 block的写法相对难记,不必司机应被,只需要在xcode里打出"inlineBlock"--回车, 系统会自动帮你把基础版写法给你匹配出来 //Block的基础声明//等号""之前是blobk的声明,等号“”后面是block的实现/*returnType:返回类型(void、int、String *…

软件无线电系列——数字调制信号的解调算法

微信公众号上线&#xff0c;搜索公众号小灰灰的FPGA,关注可获取相关源码&#xff0c;定期更新有关FPGA的项目以及开源项目源码&#xff0c;包括但不限于各类检测芯片驱动、低速接口驱动、高速接口驱动、数据信号处理、图像处理以及AXI总线等 本节目录 一、数字调制信号的解调…

Shell编程debug

debug调试 debug方法 sh -x显示脚本执行过程set命令设置开始debug和结束debug的位置显示脚本某一部分执行过程&#xff0c;解决复杂脚本故障 示例&#xff1a; sh -x 显示脚本执行过程 set显示脚本的部分执行过程 set -x 开始调试&#xff0c;从这里开始显示脚本的详细执行过…

WebAssembly 入门教程 c++、python编译wasm

WebAssembly 入门 了解 wasm 使用场景&#xff0c;复杂对象传递和经验法则。 简介 WebAssembly 是一种新的编码方式&#xff0c;可以在现代的网络浏览器中运行。它是一种低级的类汇编语言&#xff0c;具有紧凑的二进制格式&#xff0c;可以接近原生的性能运行&#xff0c;并…
最新文章