简书wordpress主题/杭州网站seo价格

简书wordpress主题,杭州网站seo价格,视频背景音乐怎么做mp3下载网站,海南省住房与城乡建设厅网站📢博客主页:https://blog.csdn.net/2301_779549673 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! 📢本文由 JohnKi 原创,首发于 CSDN🙉 📢未来很长&#…

📢博客主页:https://blog.csdn.net/2301_779549673
📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
📢本文由 JohnKi 原创,首发于 CSDN🙉
📢未来很长,值得我们全力奔赴更美好的生活✨

在这里插入图片描述

在这里插入图片描述

文章目录

  • 📢前言
  • 🏳️‍🌈一、从场景看互斥:为什么需要锁?
  • 🏳️‍🌈二、互斥锁核心概念图解
    • 2.1 临界区与非临界区
    • 2. 2 进程线程间的互斥相关背景概念
  • 🏳️‍🌈三、Linux互斥锁原理剖析
    • 3.2 初始化互斥量
    • 3.3 销毁互斥量
    • 3.3 pthread_mutex 底层实现
  • 🏳️‍🌈四、C++ RAII封装实战
    • 4.1 基础互斥类(Mutex)
    • 4.2 守卫锁(LockGuard)
  • 🏳️‍🌈五、完整代码
    • 5.1 Mutex.hpp
    • 5.2 Mutex.cc
    • 5.3 Makefile
  • 👥总结


📢前言

紧接上回的线程C++封装,这回笔者着重介绍一下互斥的原理和其必要性,并手把手使用C++封装一个RAII模型。

还有一点,笔者之后的封装都会使用之前博客中封装好的容器,需要的可以去仓库或者前面的博客中自取。

RAII 的核心思想是将资源的获取和初始化放在对象的构造函数中进行,而资源的释放放在对象的析构函数中进行。当对象被创建时,其构造函数会自动执行,从而完成资源的获取;当对象的生命周期结束时,其析构函数会被自动调用,从而完成资源的释放。这样,资源的生命周期就与对象的生命周期绑定在一起,利用 C++ 等语言的对象自动销毁机制来确保资源的正确释放。


🏳️‍🌈一、从场景看互斥:为什么需要锁?

假设你的银行账户余额是1000元,同时有两个线程执行转账操作:

  • ​线程A:存入200元 → balance += 200
  • ​线程B:取出300元 → balance -= 300
    无锁情况下可能的执行顺序:
线程A读取balance(1000) → 线程B读取balance(1000) → 
线程A写入1200 → 线程B写入700  
最终结果:700元(正确应为900元)

🏳️‍🌈二、互斥锁核心概念图解

2.1 临界区与非临界区

在这里插入图片描述

void* thread_func(void* arg) {// 非临界区(可并发执行)prepare_data();  // 临界区(需互斥访问)pthread_mutex_lock(&mtx);update_shared_resource();  pthread_mutex_unlock(&mtx);// 非临界区(可并发执行)post_process();
}

关键特征:

🔵 ​非临界区:允许多线程并行(如图中绿色区域)
🔴 ​临界区:同一时刻仅一个线程执行(红色区域)

2. 2 进程线程间的互斥相关背景概念

  • 临界资源:多线程执行流共享的资源就叫做临界资源
  • 临界区:每个线程内部,访问临界资源的代码,就叫做临界区
  • 互斥:任何时刻,互斥保证有且只有一个执行流进入临界区,访问临界资源,通常对临界资源起保护作用
  • 原子性:(后面讨论如何实现):不会被任何调度机制打断的操作,该操作只有两态,要么完成,要么未完成

🏳️‍🌈三、Linux互斥锁原理剖析

核心操作流程:

sequenceDiagramparticipant 线程Aparticipant 互斥锁participant 内核线程A->>互斥锁: pthread_mutex_lock()alt 锁空闲互斥锁-->>线程A: 立即获得锁else 锁被占线程A->>内核: 进入休眠队列内核-->>线程A: 唤醒并获取锁end线程A->>临界区: 执行操作线程A->>互斥锁: pthread_mutex_unlock()

3.2 初始化互斥量

方法一:静态分布

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER

方法二:动态分布

int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);

mutex:要初始化的互斥量
attr:NULL

3.3 销毁互斥量

销毁互斥量需要注意:

  • 使用 PTHREAD_MUTEXINITIALIZER 初始化的互斥量不需要销毁
  • 不要销毁一个已经加锁的互斥量
  • 已经销毁的互斥量,要确保后面不会有线程再尝试加锁
int pthread_mutex_destroy(pthread_mutex_t *mutex)

互斥量加锁和解锁

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
返回值:成功返回0,失败返回错误号

调⽤ pthread_mutex_lock 时,可能会遇到以下情况:

  • 互斥量处于未锁状态,该函数会将互斥量锁定,同时返回成功
  • 发起函数调用时,其他线程已经锁定互斥量,或者存在其他线程同时申请互斥量,但没有竞争到互斥量,那么pthread lock调用会陷入阻塞(执行流被挂起),等待互斥量解锁。

3.3 pthread_mutex 底层实现

// 锁结构(简化为x86实现)
struct pthread_mutex {int __lock;          // 锁状态标识int __count;         // 递归锁计数器int __owner;         // 持有者线程IDint __kind;          // 锁类型标识// ... 其他字段
};

🏳️‍🌈四、C++ RAII封装实战

4.1 基础互斥类(Mutex)

// 互斥锁封装类(不可拷贝构造/赋值)class Mutex{public:// 禁止拷贝(保护系统锁资源)Mutex(const Mutex&) = delete;const Mutex& operator = (const Mutex&) = delete;// 构造函数:初始化POSIX互斥锁Mutex(){// 初始化互斥锁属性为默认值int n = ::pthread_mutex_init(&_lock, nullptr);(void)n; // 实际开发建议处理错误码}// 析构函数:销毁锁资源~Mutex(){// 确保锁已处于未锁定状态int n = ::pthread_mutex_destroy(&_lock);(void)n; // 生产环境应检查返回值}// 加锁操作(阻塞直至获取锁)void Lock(){// 可能返回EDEADLK(死锁检测)等错误码int n = ::pthread_mutex_lock(&_lock);(void)n; // 简化处理,实际建议抛异常或记录日志}// 解锁操作(必须由锁持有者调用)void Unlock(){// 未持有锁时解锁将返回EPERMint n = ::pthread_mutex_unlock(&_lock);(void)n; }private:pthread_mutex_t _lock; // 底层锁对象};

4.2 守卫锁(LockGuard)

守卫锁不是新的锁类型,而是对已有锁的自动化生命周期管理工具。这种设计模式完美契合图示中"Lock-unlock"边界需要严格匹配的核心诉求

守卫锁工作流程

sequenceDiagramparticipant 线程participant 守卫锁participant 互斥锁线程->>守卫锁: 创建LockGuard对象守卫锁->>互斥锁: 调用Lock()互斥锁-->>守卫锁: 获得锁线程->>临界区: 执行操作线程->>守卫锁: 对象离开作用域守卫锁->>互斥锁: 调用Unlock()互斥锁-->>其他线程: 释放锁资源

实现

// RAII锁守卫(自动管理锁生命周期)class LockGuard{public:// 构造时加锁(必须传入已初始化的Mutex引用)LockGuard(Mutex &mtx):_mtx(mtx){_mtx.Lock(); // 进入临界区}// 析构时自动解锁(异常安全保证)~LockGuard(){_mtx.Unlock(); // 离开作用域自动释放}private:Mutex &_mtx; // 引用方式持有,避免拷贝导致未定义行为};

🏳️‍🌈五、完整代码

5.1 Mutex.hpp

#pragma once
#include <iostream>
#include <pthread.h> // POSIX线程库头文件namespace LockModule
{// 互斥锁封装类(不可拷贝构造/赋值)class Mutex{public:// 禁止拷贝(保护系统锁资源)Mutex(const Mutex&) = delete;const Mutex& operator = (const Mutex&) = delete;// 构造函数:初始化POSIX互斥锁Mutex(){// 初始化互斥锁属性为默认值int n = ::pthread_mutex_init(&_lock, nullptr);(void)n; // 实际开发建议处理错误码}// 析构函数:销毁锁资源~Mutex(){// 确保锁已处于未锁定状态int n = ::pthread_mutex_destroy(&_lock);(void)n; // 生产环境应检查返回值}// 加锁操作(阻塞直至获取锁)void Lock(){// 可能返回EDEADLK(死锁检测)等错误码int n = ::pthread_mutex_lock(&_lock);(void)n; // 简化处理,实际建议抛异常或记录日志}// 解锁操作(必须由锁持有者调用)void Unlock(){// 未持有锁时解锁将返回EPERMint n = ::pthread_mutex_unlock(&_lock);(void)n; }private:pthread_mutex_t _lock; // 底层锁对象};// RAII锁守卫(自动管理锁生命周期)class LockGuard{public:// 构造时加锁(必须传入已初始化的Mutex引用)LockGuard(Mutex &mtx):_mtx(mtx){_mtx.Lock(); // 进入临界区}// 析构时自动解锁(异常安全保证)~LockGuard(){_mtx.Unlock(); // 离开作用域自动释放}private:Mutex &_mtx; // 引用方式持有,避免拷贝导致未定义行为};
}

5.2 Mutex.cc

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sched.h>int ticket = 0;
pthread_mutex_t mutex;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;void *route(void *arg)
{char *id = (char *)arg;while (1){pthread_mutex_lock(&mutex);if (ticket > 0){usleep(1000);printf("%s sells ticket:%d\n", id, ticket);ticket--;pthread_mutex_unlock(&mutex);}else{printf("%s wait on cond!\n", id);pthread_cond_wait(&cond, &mutex); //醒来的时候,会重新申请锁!!printf("%s 被叫醒了\n", id);}pthread_mutex_unlock(&mutex);}return nullptr;
}int main(void)
{pthread_t t1, t2, t3, t4;pthread_mutex_init(&mutex, NULL);pthread_create(&t1, NULL, route, (void *)"thread 1");pthread_create(&t2, NULL, route, (void *)"thread 2");pthread_create(&t3, NULL, route, (void *)"thread 3");pthread_create(&t4, NULL, route, (void *)"thread 4");int cnt = 10;while(true){sleep(5);ticket += cnt;printf("主线程放票喽, ticket: %d\n", ticket);pthread_cond_signal(&cond);}pthread_join(t1, NULL);pthread_join(t2, NULL);pthread_join(t3, NULL);pthread_join(t4, NULL);pthread_mutex_destroy(&mutex);
}

5.3 Makefile

bin=testMutex
cc=g++
src=$(wildcard *.cc)
obj=$(src:.cc=.o)$(bin):$(obj)$(cc) -o $@ $^ -lpthread
%.o:%.cc$(cc) -c $< -std=c++17.PHONY:clean
clean:rm -f $(bin) $(obj).PHONY:test
test:echo $(src)echo $(obj)

👥总结

本篇博文对 从互斥原理到C++ RAII封装实践 做了一个较为详细的介绍,不知道对你有没有帮助呢

觉得博主写得还不错的三连支持下吧!会继续努力的~

请添加图片描述

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

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

相关文章

大数据-spark3.5安装部署之local模式

spark&#xff0c;一个数据处理框架和计算引擎。 下载 local模式即本地模式&#xff0c;就是不需要任何其他节点资源就可以在本地执行spark代码的环境。用于练习演示。 上传解压 使用PortX将文件上传至/opt 进入/opt目录&#xff0c;创建目录module&#xff0c;解压文件至/o…

Manus “Less structure,More intelligence ”独行云端处理器

根据市场调研机构Statista数据显示&#xff0c;全球的AR/AR的市场规模预计目前将达到2500亿美元&#xff0c;Manus作为VR手套领域的领军企业&#xff0c;足以颠覆你的认知。本篇文章将带你解读Manus产品&#xff0c;针对用户提出的种种问题&#xff0c;Manus又将如何解决且让使…

Oracle数据库存储结构--逻辑存储结构

数据库存储结构&#xff1a;分为物理存储结构和逻辑存储结构。 物理存储结构&#xff1a;操作系统层面如何组织和管理数据 逻辑存储结构&#xff1a;Oracle数据库内部数据组织和管理数据&#xff0c;数据库管理系统层面如何组织和管理数据 Oracle逻辑存储结构 数据库的逻…

芯驿电子 ALINX 亮相德国纽伦堡,Embedded World 2025 精彩回顾

2025年3月13日&#xff0c;全球规模最大的嵌入式行业盛会——德国纽伦堡国际嵌入式展&#xff08;embedded world 2025&#xff09;圆满落幕。 在这场汇聚全球 950 家展商、3 万余专业观众的科技盛宴中&#xff0c;芯驿电子 ALINX 展位人头攒动&#xff0c;多款尖端产品吸引客户…

Nexus File类型Blob Stores迁移至Minio操作指南(上)

#作者&#xff1a;闫乾苓 文章目录 目的前期准备查看file类型Blob Stores数据目录位置aws cli客户端连接工具OrientDB cli客户端连接工具在minio中新建 bucket 目的 增强nexus构件数据的高可用性和扩展性 前期准备 查看并记录需要迁移的Blob Store及repository 查看fil…

蓝桥杯嵌入式组第十二届省赛题目解析+STM32G431RBT6实现源码

文章目录 1.题目解析1.1 分而治之&#xff0c;藕断丝连1.2 模块化思维导图1.3 模块解析1.3.1 KEY模块1.3.2 LED模块1.3.3 LCD模块1.3.4 TIM模块1.3.5 UART模块1.3.5.1 uart数据解析 2.源码3.第十二届题目 前言&#xff1a;STM32G431RBT6实现嵌入式组第十二届题目解析源码&#…

【MySQL】表的约束(上)

文章目录 表的约束什么是表的约束空属性默认值列描述&#xff08;comment&#xff09;零填充&#xff08;zerofill&#xff09;主键 总结 表的约束 什么是表的约束 表的约束&#xff08;Constraints&#xff09;是数据库表中的规则&#xff0c;用于限制存储的数据&#xff0c…

【Unity网络同步框架 - Nakama研究(三)】

文章目录 【Unity网络同步框架 - Nakama研究(三)】准备工作前言Unity部分连接服务器创建并进入房间创建人物人物移动和同步 【Unity网络同步框架 - Nakama研究(三)】 以下部分需要有一定的Unity基础&#xff0c;在官方的案例Pirate Panic基础上进行修改而成。如果没有下载并熟悉…

【AD】6-1 PCB常用规则

间距规则&#xff1a; 可自行修改线宽与间距&#xff08;默认10mil&#xff09; 线宽规则&#xff1a;电源线宽加粗 布线过程中更改线宽&#xff1a;走线状态下&#xff0c;shiftw更改线宽&#xff0c;线宽要在规则范围之内过孔规则&#xff1a; 阻焊规则&#xff1a;

【零基础入门unity游戏开发——unity3D篇】3D物理系统之 —— 3D刚体组件Rigidbody

考虑到每个人基础可能不一样,且并不是所有人都有同时做2D、3D开发的需求,所以我把 【零基础入门unity游戏开发】 分为成了C#篇、unity通用篇、unity3D篇、unity2D篇。 【C#篇】:主要讲解C#的基础语法,包括变量、数据类型、运算符、流程控制、面向对象等,适合没有编程基础的…

C# net deepseek RAG AI开发 全流程 介绍

deepseek本地部署教程及net开发对接 步骤详解&#xff1a;安装教程及net开发对接全流程介绍 DeepSeekRAG 中的 RAG&#xff0c;全称是 Retrieval-Augmented Generation&#xff08;检索增强生成&#xff09;&#xff0c;是一种结合外部知识库检索与大模型生成能力的技术架构。其…

b站视频下载工具软件怎么下载

自行配置FFMPEG环境 请优先选择批量下载&#xff0c;会自处理视频和音频文件。 如果要下载更高质量请登陆。 没有配置FFMPEG下载后会有报错提示&#xff0c;视频音频文件无法合并生成mp4文件 更新批量下载标题&#xff0c;只取视频原标题&#xff0c;B站反爬机制登陆后下载多了…

Java 大视界 -- Java 大数据分布式计算中的资源调度与优化策略(131)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

时序数据库 TDengine 到 MySQL 数据迁移同步

简述 TDengine 是一款开源、高性能、云原生的时序数据库&#xff0c;专为物联网、车联网、工业互联网、金融、IT 运维等场景优化设计。在工业自动化的时代&#xff0c;时序数据库在电力、轨道交通、智能制造等领域有着广泛的应用。 MySQL 是全球广泛使用的开源关系型数据库&a…

基于YOLO11深度学习的舌苔舌象检测识别与诊断系统【python源码+Pyqt5界面+数据集+训练代码】

《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…

正新鸡排:在变革浪潮中领航,打造连锁餐饮新生态

在当下风云变幻的餐饮市场中&#xff0c;连锁品牌犹如逆水行舟&#xff0c;不进则退。作为国内坐拥万店的知名连锁餐饮品牌&#xff0c;正新鸡排2023年卖出了7.2亿片鸡排&#xff0c;集团营收同比增长28%。在《2024年中国鸡排连锁品牌10强榜单》中&#xff0c;正新鸡排以高达95…

软件性能测试与功能测试联系和区别

随着软件开发技术的迅猛发展&#xff0c;软件性能测试和功能测试成为了确保软件质量的两个重要环节。那么只有一字之差的性能测试和功能测试分别是什么?又有哪些联系和区别呢? 一、软件性能测试是什么?   软件性能测试是为了评估软件系统在特定条件下的表现&#xff0c;包…

Vue 框架使用难点与易错点剖析:避开陷阱,提升开发效率

Vue.js 作为当下最流行的前端框架之一&#xff0c;以其轻量、易用和灵活的特性深受开发者喜爱。然而&#xff0c;即使是经验丰富的开发者&#xff0c;在使用 Vue 的过程中也难免会遇到一些难点和易错点。本文将深入分析 Vue 开发中常见的“坑”&#xff0c;并提供解决方案和代码…

Odoo Http鉴权+调用后端接口

最近在调研Odoo18&#xff0c;包括它的前后端原理、源码等。发现官方的开发文档并不十分实用&#xff0c;比如标题这种简单的实用需求&#xff0c;竟然浪费了一点时间&#xff0c;特此记录。 官方文档&#xff1a;External API — Odoo 18.0 documentation 前提&#xff1a;首…

Linux常见问题与分析

操作系统进行线程切换时进行的动作 1. 保存当前线程的上下文 寄存器状态&#xff1a;保存 CPU 寄存器&#xff08;如通用寄存器、程序计数器 PC、栈指针 SP 等&#xff09;到当前线程的 线程控制块&#xff08;TCB&#xff09; 中。内核栈信息&#xff1a;如果线程在内核态运…