当前位置: 首页 > news >正文

Linux线程3.0-线程同步与互斥,C/C++互斥锁。

@bit::Shadow
✧(≖ ◡ ≖✿

目录

原子 / 非原子

@原子操作对信号怎么处理?

非原子操作的矛盾

互斥锁

解决的问题

锁的本质

接口

定义锁

全局定义

局部定义

释放

加锁、解锁

@为什么加锁解锁都是原子的呢?

@加锁之后临界区内线程切换会怎么样?

多线程模拟并发

视频演示无锁将票抢到负数

视频演示有锁正常抢票

互斥锁的理解

执行流程

C++下的锁

互斥锁

RAII锁最常用的锁99%


原子 / 非原子

以 " a-- " 为例,探究原子与非原子。

原子操作的本质:给CPU发出指令对于“a”的修改不允许任何外部事件打断也不允许被其他核心看到中间状态。

@原子操作对信号怎么处理?

以原子操作为优先,信号先被挂起。// kill -9 [pid]等硬核信号除外。

非原子操作的矛盾

以a--为例:

  1. 读取(Load):CPU 将a的值(3)从主内存加载到它内部的寄存器(如eax)中。

  2. 修改(Modify):CPU 在寄存器中执行eax = eax - 1,此时eax的值变为 2。

  3. 写入(Store):CPU 将寄存器中的新值(2)写回主内存中a所在的内存地址。

在以上非原子操作中,若出现并发问题那么执行流就可能发生混乱而造成预料之外的结果。

解决方法——创建互斥锁来限制线程的并发

互斥锁

解决的问题

  • 轻量级进程间信息过于同步而导致同时访问的问题。
  • 多线程下临界区内非原子性操作带来的执行流内指令重置混乱的问题。

锁的本质

☆互斥锁lock()的本质是,将原先并行的轻量级进程改为串行

接口

pthread_mutex_t

p:POSIX(portable Operating Systemd interface)可移植操作系统接口。

mutex:(mutual exclusion)互斥。

t:(type)类型。

定义锁

锁既需要定义还需要释放

全局定义

互斥锁在全局进行定义初始化

pthread_mutex_t mutex = PTHREAD_MUTEX_INITLIZER;
局部定义

必须使用pthread_mutex_init()

pthread_mutex_t mutex; int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attr);
  1. *mutex:&锁。
  2. *attr:锁的属性,nullptr即可。

释放

int pthread_mutex_destroy(pthread_mutex_t* mutex);

加锁、解锁

不论加锁还是解锁均是原子的

phtread_mutex_lock(pthread_mutex_t* mutex); phtread_mutex_unlock(pthread_mutex_t* mutex);

加锁失败会阻塞挂起执行流。

@为什么加锁解锁都是原子的呢?

“上锁”的目的就是“保证锁内区域所有操作均是‘原子’的”,而只有持锁线程完毕才能“放下锁”——下一线程持锁进入锁区。若加锁不是原子的,那么也就导致了“持锁时并发混乱问题”。

@加锁之后临界区内线程切换会怎么样?

线程主导更换,但持锁线程不变。

多线程模拟并发

3个线程同时抢票

#include<unistd.h> #include<iostream> #include<pthread.h> static int ticket = 10000; void* routine(void* arg) { while (ticket) { //等待一会儿确保多个线程进来 usleep(500);//500ms printf("%d\n", --ticket); } return arg; } int main() { //3个新线程抢1000票 pthread_t pd1,pd2,pd3; pthread_create(&pd1, nullptr, routine, (void*)"pthread_1"); pthread_create(&pd2, nullptr, routine, (void*)"pthread_2"); pthread_create(&pd3, nullptr, routine, (void*)"pthread_3"); pthread_join(pd1, nullptr); pthread_join(pd2, nullptr); pthread_join(pd3, nullptr); return 0; }

视频演示无锁将票抢到负数

无锁多线程抢票

发现循环一直进行不会终止。

原因分析🔍

while(ticket)仅当0时判断为假usleep(500)下足够线程进入循环内会将票数直接减为负数后再出循环判断。

在原子操作区加互斥锁即可

static int ticket = 500; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void* routine(void* arg) { while (ticket) { pthread_mutex_lock(&mutex); if(ticket == 0) { //先前线程已然将票抢完 这些线程就解锁并退出 pthread_mutex_unlock(&mutex); break; } //等待一会儿确保多个线程进来 usleep(1000);//500ms printf("%d\n", --ticket); if(ticket == 0) pthread_mutex_unlock(&mutex); pthread_mutex_unlock(&mutex);// ticket>0解锁其他线程进来 } return arg; }

视频演示有锁正常抢票

加锁抢票

互斥锁的理解

锁限制了“原子性”标志的掌握者,下图中%al是寄存器的一种。

执行流程

1.将寄存器内数据置零。
2.交换寄存器内数据与内存。
3.根据持锁(内存中的1标志),情况决定线程行为。

C++下的锁

互斥锁

最基础的独占锁,只有一个线程可以占用。

//定义 std::mutex mutex; //加锁、解锁 mutex.lock(); mutex.unlock();

RAII锁最常用的锁99%

⚛️使用RAII原则“资源获取即初始化”,构造时自动加锁,出作用域自动解锁。(本质是封装以上述C接口的类,以调用相应的构造函数、析构函数)

std::lock_guard<std::mutex>(mtx);

由于lock_guard是作用域相关所以常常需要{}来限定。

感谢支持,持续更新
欢迎关注

http://www.jsqmd.com/news/1070923/

相关文章:

  • 关于GraalVM的说明
  • 豆包导出pdf怎么调顺序?试试AI 导出鸭智能排序
  • 联邦学习实战:破解非独立同分布数据困局的算法策略与调优指南
  • 鸿蒙PC适配llvm-gcc-compat编译安装第三方库itertools,打造Rust 第三方迭代器增强库
  • 东莞企业做GEO为什么AI不推荐你-信源权重的3个硬指标拆开看
  • 基于MobileNetV3的轻量化人脸年龄估计模型:MobileAgeNet实战指南
  • AI开发-多路径写入一致性:从一次 Debug 到系统性防御
  • 从“会聊天“到“能干活“:用 OpenCode 给自己找个 AI 搭子
  • 【收藏夹必备】写博文还在用“●“做列表?这些图标让文章质感翻倍!
  • 【硬核长文】万字拆解无线网络核心:AP(无线访问接入点)从底层原理到企业级实战调优指南
  • HoRain云--R语言列表操作全指南:从入门到精通
  • 无人机遥感国土目标检测数据集 无人机耕地数据集 无人机道路农田检测 国土遥感地物实例分割数据集 yolo数据集第10759期
  • 五、进程控制
  • 程序员面试翻车?我用了两个月测遍AI面试工具,最终只推荐这一个
  • Redis...2
  • 影刀RPA新手教程:零基础入门完全指南——从下载安装到独立开发你的第一个自动化流程
  • Ntk-aware 插值
  • 医疗AI多模态学习:M-IDoL框架突破信息模糊困境
  • 流体-结构耦合与声子亚表面在湍流减阻中的应用
  • 反射的定义、使用方式、优缺点和具体使用场景
  • RFID 仓库管理系统 项目总结
  • 基于用户画像的AI内容生成与安全检测闭环系统实践
  • 外部中断EXTI和NVIC
  • 模块化驱动架构,升级不再怕冲突
  • 高级java每日一道面试题-2026年02月26日-实战篇[Docker]-如何实现镜像的合规性检查(如金融行业的基线要求)?
  • 智能体进化与上下文管理:GA如何通过失败升级与内存压缩实现高效学习
  • 【日耕一题】7. 循环右移(2026第17届蓝桥杯C++B组省赛 C 题)
  • MyBatis SQL映射配置全解析:XML配置、动态SQL与注解驱动深度实战指南
  • 问卷也能做高颜值?问卷星、金数据等5大平台美工设计能力实测
  • 教育视频摘要技术TR-EduVSum的创新与应用