C++之互斥锁、读写锁、互斥量、 信号量、原子锁机制总结(二百二十五)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长!

优质专栏:Audio工程师进阶系列原创干货持续更新中……】🚀

人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.

更多原创,欢迎关注:Android系统攻城狮

欢迎关注Android系统攻城狮

1.前言

本篇目的:C++之互斥锁、读写锁、互斥量、 信号量、原子锁机制用法.

互斥锁、读写锁、互斥量、信号量和原子锁都是并发编程中常用的同步机制。

  1. 互斥锁(Mutex Lock)是最常见的同步机制之一,它用于保护共享资源,只允许一个线程访问资源,其他线程需要等待。互斥锁有两种状态:加锁和解锁。当一个线程获得了互斥锁并执行代码时,其他线程会被阻塞,直到锁被释放。通过互斥锁可以避免多个线程同时访问共享资源而导致的数据竞争问题。

  2. 读写锁(Read-Write Lock)允许多个线程同时读取共享资源,但只有一个线程可以写入资源。读写锁在读取操作频繁、写入操作较少的场景下可以提高并发性能。当有线程正在写入资源时,其他线程无法读取或写入资源,读写锁会保持写锁的状态,直到写操作完成。

  3. 互斥量(Mutex)和互斥锁类似,也用于保护共享资源。互斥量是一种更通用的同步机制,可以实现更复杂的同步逻辑。与互斥锁不同的是,互斥量可以由多个线程共享。

  4. 信号量(Semaphore)是一种计数器,用于控制对共享资源的访问。它可以控制同时访问资源的线程数量,并提供了同步和互斥的机制。信号量有两种操作:P操作(等待)和V操作(释放)。当信号量的计数器为0时,执行P操作的线程会被阻塞,直到计数器大于0时才能继续执行。

  5. 原子锁(Atomic Lock)是一种基于原子操作的同步机制,用于保护对共享资源的访问。原子操作是指不会被其他线程中断的操作。原子锁能够确保在同一时刻只有一个线程可以执行临界区代码,从而避免了竞态条件的发生。

2.应用实例

<1>. 互斥锁(Mutex Lock)的作用是保护共享资源,确保在同一时间只有一个线程可以访问该资源。

#include <iostream>
#include <mutex>std::mutex mtx;void sharedResourceAccess() {// 锁定互斥锁,确保只有一个线程可以进入临界区mtx.lock();// 访问共享资源的代码// ...// 解锁互斥锁,允许其他线程访问共享资源mtx.unlock();
}int main() {// 创建多个线程并发访问共享资源// ...return 0;
}
  • 在C++中,互斥锁(Mutex)是一种用来实现线程同步的机制,用于保护临界区(Critical Section)的同步访问。互斥锁的原理是通过对共享资源加锁来确保同时只有一个线程可以访问该资源。

  • 互斥锁的使用包括两个主要步骤:加锁和解锁。当一个线程需要访问临界区时,会先尝试加锁,如果成功获取到锁,则可以进入临界区执行操作。在临界区执行完毕后,线程会释放锁,允许其他线程获取锁并进入临界区。

<2>. 读写锁(Read-Write Lock)的作用是允许多个线程同时读取共享资源,但只有一个线程可以写入资源。

#include <iostream>
#include <shared_mutex>std::shared_mutex rwMutex;void readAccess() {// 读操作,可以同时由多个线程进入临界区std::shared_lock<std::shared_mutex> readLock(rwMutex);// 读取共享资源的代码// ...
}void writeAccess() {// 写操作,只有一个线程可以进入临界区std::unique_lock<std::shared_mutex> writeLock(rwMutex);// 写入共享资源的代码// ...
}int main() {// 创建多个线程并发读写共享资源// ...return 0;
}
  • C++中的读写锁(Read-Write Lock)是一种用于控制对共享资源的并发访问的锁机制。相比于互斥锁,在读写锁中允许并发地执行多个读操作,但在写操作时需要独占访问。

  • 读写锁的原理是根据访问类型(读操作或写操作)来决定锁的状态和行为。它的主要特点包括:

  1. 多个读操作可以同时进行,互不干扰。当没有线程在写操作时,允许多个线程同时获取读锁。
  2. 写操作需要独占地访问,此时不允许其他读操作或写操作。
  3. 当有线程持有读锁时,写锁将一直处于阻塞状态,直到所有持有的读锁都被释放。### <3>. 互斥量(Mutex)的作用与互斥锁类似,也用于保护共享资源。互斥量可以由多个线程共享,其用法与互斥锁类似。

<3>.互斥量

#include <iostream>
#include <mutex>std::mutex mtx;void sharedResourceAccess() {// 锁定互斥量,确保只有一个线程可以进入临界区std::lock_guard<std::mutex> lock(mtx);// 访问共享资源的代码// ...
}int main() {// 创建多个线程并发访问共享资源// ...return 0;
}
  • C++中的互斥量(Mutex)是一种用于实现互斥访问的锁机制,用来保护共享资源免受并发访问的干扰。

  • 互斥量的原理是基于一个简单的概念,即同一时间只有一个线程可以持有该互斥量的锁。当一个线程获取了互斥量的锁之后,其他线程就无法获取该互斥量的锁,只能等待锁的释放。

互斥量实现互斥访问的方式可以分为两类:

  1. 阻塞式:当一个线程尝试获取互斥量的锁,而锁已经被其他线程获取时,该线程会被阻塞,直到锁被释放。这样可以确保同一时间只有一个线程对共享资源进行访问,但存在可能产生死锁的风险。

  2. 非阻塞式:当一个线程尝试获取互斥量的锁,如果锁已经被其他线程获取,则该线程会得到一个错误码或标识,而不会被阻塞。通过不断地尝试获取锁的方式,线程可以在后续的时刻再次尝试获取锁。

使用互斥量可以确保在单个时间点只有一个线程可以访问共享资源,从而避免了并发访问导致的数据竞争和不确定性。在使用互斥量时,需要谨慎选择锁的粒度和获取锁的时机,以最大程度地减少锁的竞争和阻塞时间,提高程序的并发性和性能。

<4>. 信号量(Semaphore)的作用是控制对共享资源的访问,限制同时访问资源的线程数量。

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>std::mutex mtx;
std::condition_variable cv;
int counter = 0;void accessResource() {std::unique_lock<std::mutex> lock(mtx);// 等待信号量,当 counter 大于 0 时继续执行cv.wait(lock, [] { return counter > 0; });// 访问共享资源的代码// ...// 释放信号量,计数器减一counter--;
}void releaseResource() {std::unique_lock<std::mutex> lock(mtx);// 增加信号量,计数器加一counter++;// 通知等待的线程可以继续执行cv.notify_one();
}int main() {// 创建多个线程并发访问共享资源// ...return 0;
}
  • C++中的信号量(Semaphore)是一种用于控制并发访问的同步机制,用来管理对共享资源的访问权限。

  • 信号量的原理是基于一个计数器和等待队列。信号量内部维护一个整型计数器,用于记录可用资源的数量。当线程需要访问共享资源时,它会尝试获取一个资源,即将信号量的计数器减一。如果计数器大于等于零,则线程成功获取了一个资源,并可以继续执行;如果计数器小于零,则表示资源已被占用,该线程需要等待。

当一个线程使用完资源后,它会释放掉该资源,并将信号量的计数器加一。如果此时有其他线程正在等待资源,那么其中一个线程将被唤醒,并可以继续执行。

信号量的计数器可以表示为正整数,也可以是非负整数。在使用时,信号量的初始值可以根据需求设置。如果初始值为1,则信号量可以用作互斥锁;如果初始值大于1,则信号量可以用作限制并发访问的资源数。

信号量可以用于解决一些典型的并发访问问题,如有限缓冲区的生产者消费者问题、并发任务的调度和同步等。

<5>.原子锁,用于保护对共享资源的访问,原子操作是指不会被其他线程中断的操作。

#include <iostream>
#include <atomic>
#include <thread>// 创建一个原子锁
std::atomic<bool> atomic_lock(false);void criticalSection() {while (atomic_lock.exchange(true)) {// 原子锁正在被其他线程使用,等待...}// 进入临界区,访问共享资源std::cout << "进入临界区,访问共享资源" << std::endl;// 模拟一段临界区代码std::this_thread::sleep_for(std::chrono::seconds(2));// 退出临界区,释放原子锁atomic_lock.store(false);std::cout << "退出临界区,释放原子锁" << std::endl;
}int main() {// 创建多个线程并发访问临界区std::thread t1(criticalSection);std::thread t2(criticalSection);// 等待线程完成t1.join();t2.join();return 0;
}
  • C++中的原子锁是一种用于实现线程安全的同步机制,也称为原子操作。它提供了一种保证特定代码区域在任意时间点只能被一个线程执行的能力。

  • 原子锁的实现原理是基于硬件的原子操作指令或操作系统提供的原子操作接口。通过这些机制,可以确保在多线程环境下,对共享资源的访问是原子性、无竞争的。

  • 原子锁的一种常见实现方式是使用互斥量(Mutex)。当一个线程尝试获取原子锁时,如果锁已经被其他线程获取,则该线程会被阻塞,直到锁被释放。这样可以确保在任意时间点只有一个线程可以执行被保护的代码区域。

  • 原子锁还可以采用其他实现方式,如使用硬件原子操作指令或操作系统提供的原子操作接口。这些方式在底层实现上比互斥量更高效,减少了阻塞和唤醒线程的开销。

  • 原子锁的使用可以提供更细粒度的同步控制,避免了大范围的阻塞,从而提高并发性和性能。然而,在使用原子锁时需要注意避免死锁和资源竞争的问题。

  • 过度使用原子锁可能会导致性能下降,因为它们在竞争激烈的情况下可能会导致线程阻塞和唤醒的频繁切换。因此,在设计多线程程序时,需要综合考虑并发性、性能和代码复杂性等因素,选择合适的同步机制来保护共享资源的访问。

3.自定义实现Mutex自动加锁和解锁

#include <iostream>
#include <mutex>class Mutex {
public:void lock() {// 实现互斥锁的加锁操作std::cout << "Mutex locked" << std::endl;}void unlock() {// 实现互斥锁的解锁操作std::cout << "Mutex unlocked" << std::endl;}
};class Autolock {
public:explicit Autolock(Mutex& mutex) : mLock(mutex) {mLock.lock();}~Autolock() {mLock.unlock();}private:Mutex& mLock;
};int main() {Mutex mutex;{Autolock autolock(mutex);  // 创建Autolock对象,自动加锁// 执行在临界区内的操作std::cout << "In critical section" << std::endl;}  // Autolock对象销毁,自动解锁return 0;
}```
`注意:使用构造函数和析构函数实现Mutex自动加锁和解锁。`

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

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

相关文章

MAC word 如何并列排列两张图片

系统&#xff1a;MAC os 参考博客 https://baijiahao.baidu.com/s?id1700824516945958911&wfrspider&forpc 步骤1 新建一个word文档和表格 修改表格属性 去掉自动重调尺寸以适应内容 插入图片 在表格的位置插入对应的图片如下 去除边框 最终结果如下

UE5 ChaosVehicles载具研究

一、基本组成 载具Actor类名称&#xff1a;WheeledVehiclePawn Actor最原始的结构 官方增加了两个摇臂相机&#xff0c;可以像驾驶游戏那样切换多机位、旋转观察 选择骨骼网格体、动画蓝图类、开启物理模拟 二、SportsCar_Pawn 角阻尼&#xff1a;物体旋转的阻力。数值越大…

云原生技术盛会KubeCon即将召开!亚马逊云科技作为钻石赞助商参会

KubeCon2023将于9月26-28日在上海跨国采购会展中心隆重召开。作为云原生领域最负盛名的技术大会之一&#xff0c;KubeConCloudNativeCon是连接全球开发者与云原生社区的最佳平台&#xff0c;此次还新增Open Source Summit环节&#xff0c;吸引了全球顶尖的云原生专家们汇聚其中…

数据链路层协议

文章目录 数据链路层协议0. 数据链路层解决的问题1. 以太网协议(1) 认识以太网(2) 以太网帧格式<1> 两个核心问题 (3) 认识MAC地址(4) 局域网通信原理(5) MTU<1> 认识MTU<2> MTU对IP协议的影响<3> MTU对UDP协议的影响<4> MTU对TCP协议的影响<…

Python开发与应用实验2 | Python基础语法应用

*本文是博主对学校专业课Python各种实验的再整理与详解&#xff0c;除了代码部分和解析部分&#xff0c;一些题目还增加了拓展部分&#xff08;⭐&#xff09;。拓展部分不是实验报告中原有的内容&#xff0c;而是博主本人自己的补充&#xff0c;以方便大家额外学习、参考。 &a…

安装Python3.x--Windows

1 下载安装包 确定安装是干什么&#xff0c;要下哪个版本&#xff08;如果是配置项目环境&#xff0c;最好按项目需求的版本来装&#xff09; 1.1 官网链接 https://www.python.org 最新版本 指定版本 2 安装说明 点击下载exe&#xff0c;运行自定义安装路径&#xff0c;下…

TS编译选项——不允许使用隐式any类型、不明确类型的this、严格检查空值、编译后文件自动设置严格模式

一、不允许使用隐式any类型 在tsconfig.js文件中配置noImplicitAny属性 {"compilerOptions": {// 不允许使用隐式any类型"noImplicitAny": true} } 开启后即可禁止使用隐式的any类型 注意&#xff1a;显式的any类型并不会被禁止 二、不允许使用不明确类…

腾讯mini项目-【指标监控服务重构】2023-08-26

今日已办 Venus 的 Trace 无感化 定义 handler 函数 fiber.Handler 的主要处理逻辑返回处理中出现的 error返回处理中响应 json 的函数 // handler // Description: // Author xzx 2023-08-26 18:00:03 // Param c // Return error // Return func() error : function for …

和 Node.js 说拜拜,Deno零配置解决方案

不知道大家注意没有&#xff0c;在我们启动各种类型的 Node repo 时&#xff0c;root 目录很快就会被配置文件塞满。例如&#xff0c;在最新版本的 Next.js 中&#xff0c;我们就有 next.config.js、eslintrc.json、tsconfig.json 和 package.json。而在样式那边&#xff0c;还…

Spring面试题9:Spring的BeanFactory和FactoryBean的区别和联系

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:说一说Spring的BeanFactory和FactoryBean的区别和联系 区别:BeanFactory是一个工厂接口,主要负责管理和创建Bean实例。它是Spring提供的最底层的…

优维产品最佳实践:主机合规性检查

我们常常会感到这样的困惑&#xff0c;为什么这么多的无效主机记录&#xff0c;为什么这些主机很多信息空白&#xff0c;当许多人一起维护主机信息时&#xff0c;常常会出现信息错漏的情况。主机是运维最重要最基本的CMDB信息&#xff0c;而「合规性检查」为我们提供了更高效便…

Spring Cloud Alibaba Gateway 简单使用

文章目录 Spring Cloud Alibaba Gateway1.Gateway简介2. 流量网关和服务网关的区别3. Spring Cloud Gateway 网关的搭建3.1 Spring Cloud Gateway 配置项的说明3.2 依赖导入3.3 配置文件 Spring Cloud Alibaba Gateway 1.Gateway简介 Spring Cloud Gateway是一个基于Spring F…

混合Rollup:探秘 Metis、Fraxchain、Aztec、Miden和Ola

1. 引言 混合Rollup为新的以太坊L2扩容方案&#xff0c;其分为2大类&#xff1a; 将乐观与ZK技术结合的混合Rollup同时支持公开智能合约 和 私人智能合约 的混合Rollup 本文将重点关注Metis、Fraxchain、Aztec、Miden和Ola这五大项目。 2. 何为混合Rollup&#xff1f; 混合…

MySQL-树型结构数据查询

表结构 进行树形结构查询&#xff0c;利用特殊语法进行操作 with recursive t as(select parent_id , business_namefrom business_line where id 21union allselect a.parent_id, a.business_namefrom business_line a join t on a.id t.parent_id) select business_name f…

[AI Agent学习] MetaGPT源码浅析

前言 工作上&#xff0c;需要使用AI Agent&#xff0c;所以需要深入学习一下AI Agent&#xff0c;光阅读各种文章&#xff0c;总觉无法深入细节&#xff0c;所以开看各类AI Agent相关的开源项目&#xff0c;此为第一篇&#xff0c;学习一下MetaGPT的源码。 基本目标 MetaGPT是一…

【深度学习实验】前馈神经网络(八):模型评价(自定义支持分批进行评价的Accuracy类)

目录 一、实验介绍 二、实验环境 1. 配置虚拟环境 2. 库版本介绍 三、实验内容 0. 导入必要的工具包 1. __init__(构造函数) 2. update函数(更新评价指标) 5. accumulate(计算准确率) 4. reset(重置评价指标) 5. 构造数据进行测试 6. 代码整合 一、实验介绍 本文将实…

进行 XSS 攻击 和 如何防御

跨站脚本攻击&#xff08;XSS 攻击&#xff09;是 Web 开发中最危险的攻击之一。以下是它们的工作原理以及防御方法。 XSS 攻击 跨站脚本攻击就是在另一个用户的计算机上运行带有恶意的 JS 代码。假如我们的程序没有对这些恶意的脚本进行防御的话&#xff0c;他们就会由我们的…

李宏毅hw-10 ——adversarial attack

一、查漏补缺&#xff1a; 1.关于glob.glob的用法&#xff0c;返回一个文件路径的 列表&#xff1a; 当然&#xff0c;再套用1个sort&#xff0c;就是将所有的文件路径按照字母进行排序了 2.relpath relative_path返回相对于基准路径的相对路径的函数 二、代码剖析&#xff…

STM32单片机入门学习(四)-蜂鸣器

蜂鸣器接线 低平蜂鸣器&#xff0c;低电平发声&#xff0c;高电平不发声&#xff0c; 三个排针&#xff0c;VCC接3.3v&#xff0c;GND接地&#xff0c;I/O接A0口&#xff0c;如图&#xff1a; 蜂鸣器代码&#xff1a;响一秒停半秒 #include "stm32f10x.h" #includ…

LRU、LFU 内存淘汰算法的设计与实现

1、背景介绍 LRU、LFU都是内存管理淘汰算法&#xff0c;内存管理是计算机技术中重要的一环&#xff0c;也是多数操作系统中必备的模块。应用场景&#xff1a;假设 给定你一定内存空间&#xff0c;需要你维护一些缓存数据&#xff0c;LRU、LFU就是在内存已经满了的情况下&#…