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

【c++与Linux进阶】线程篇 -互斥锁

1. 前言在我们之前学习的代码种就是在建造多线程的路上我们可以看到出现了乱码或者抢占输出这是为什么呢本章将带着这个问题来带你思考一个例子先来领略问题的所在。什么是线程互斥.见识互斥锁。使用互斥锁2. 一个买票的例子假设我们有100张电影票我们同时抢票会出现什么我们来尝试写代码来看看#includeiostream#includethread#includevector#includestring#includecstdio#includeunistd.hintticket100;voidroutine(std::string name){while(1){if(ticket0){usleep(1000);// 说明可以开始抢票ticket--;printf(%s shell ticket,now tickets number:%d\n,name.c_str(),ticket);}else{std::coutticketstd::endl;break;}}return;}intmain(){std::vectorstd::threadthreads;for(inti0;i5;i){std::string namethread-;namestd::to_string(i);threads.emplace_back(routine,name);}for(autothread:threads){thread.join();}return0;}这里的公共的资源是ticket很显然是五个线程去抢这个票数其中我们用usleep1000来表示抢票消耗的时间。按照常理来说我们一旦没票了就应该停止。让我们运行来看看结果会运行到 -4我的票都没有了这为什么会运行成为这样嘞2-1 原因如果是单线程来说是不会发生这件事的但是这里是多线程多线程很大的一个特点就是竞争。我们来看我们的代码我们每个线程进入这个函数都会拿到ticket的数量。随后休息一秒钟在进行对其减减。我们放慢过程详细的来看看当票数为1的时候的情况我们可以假设线程1拿到ticket票之后发现是1随后休息1秒随后线程2启动发现这个ticket也是1也是可以进行减减。其中线程2也会休息一秒。我们线程1在拿入ticket在进行减减导致变成0。关键的来了由于线程2之前做过了判断。可以进行减减我们在对ticket进行减减就导致变成了-1。为什么是这样的过程我来大致写写; if (ticket 0) LOAD R1, [ticket] ; R1 ticket CMP R1, 0 ; 比较 R1 和 0 JLE END_IF ; 如果 0跳走 ; usleep(1000) CALL usleep ; ticket-- LOAD R2, [ticket] ; R2 当前 ticket SUB R2, 1 ; R2 R2 - 1 STORE [ticket], R2 ; 写回 ticket END_IF:注意这里最重要的一点判断时用的是R1真正减法时又重新LOAD R2, [ticket]读了一次内存我上面写的可能有歧义但是我们在联系汇编来详细的讲讲线程1启动发现ticket是1可以进行减减执行usleep1000。注意这里比较分三步进入寄存器比较从寄存器种写回。线程2启动发现ticket是1可以进行减减执行usleep1000。关键的来了线程1对其减减。这个减减是进入寄存器对进行减减在写回ticket这个ticket已经发生改变了那么线程2拿到的ticket就是已经被线程1改变的ticket了线程2对之后写回的ticket进行减减这个就是经典的check-then-act race。3. 引入锁的概念为了防止上面的乌龙的事件我们引入了锁的概念先不说是什么我们先来看看他的威力#includeiostream#includethread#includevector#includestring#includecstdio#includeunistd.hintticket100;pthread_mutex_t lockPTHREAD_MUTEX_INITIALIZER;voidroutine(std::string name){while(1){pthread_mutex_lock(lock);if(ticket0){usleep(1000);//说明可以开始抢票ticket--;printf(%s shell ticket,now tickets number:%d\n,name.c_str(),ticket);pthread_mutex_unlock(lock);}else{pthread_mutex_unlock(lock);break;}}return;}intmain(){std::vectorstd::threadthreads;for(inti0;i5;i){std::string namethread-;namestd::to_string(i);threads.emplace_back(routine,name);}for(autothread:threads){thread.join();}return0;}我们来看看代码运行的情况我们可以看到这个是没有问题的的确完成了检票的任务。那么这里的锁是什么互斥锁 (Mutex)特点“互斥”即其名同一时间只有一个线程能持有锁。用法pthread_mutex_lock()加锁pthread_mutex_unlock()解锁。这里就是全局锁的初始化和上锁和解锁。3-1互斥锁上锁的位置我们先来回忆为什么需要上锁是不是由于线程出现竞争导致公共资源出现混乱所以一切访问公共资源的地方都需要上锁一次只允许一个线程去访问使用。那么我讲的这些就是临界区的概念 什么是临界区 (Critical Section)临界区是指代码中访问共享资源如全局变量、外部文件、共享内存等的那一部分程序段。核心规则同一时刻只允许一个线程进入临界区。如果不保护就会发生“竞态条件”Race Condition导致数据毁坏。保护方式进入临界区前加锁Lock离开临界区后解锁Unlock。3-2 解锁的时机我们可以看到我的代码无论是在if还是else我们都会解锁就是解除锁有人就说了为什么不像后面这个代码一样直接解除锁呢pthread_mutex_lock(lock);if(ticket0){//说明可以开始抢票ticket--;printf(%s shell ticket,now tickets number:%d\n,name.c_str(),ticket);//pthread_mutex_unlock(lock);}else{//pthread_mutex_unlock(lock);break;}pthread_mutex_unlock(lock);那么else就永远不会解锁他直接break这就会导致出现另一个问题这正是一种典型的死锁诱因一个线程在持有锁的情况下直接退出如 break、return 或异常而未释放锁导致其他需要该锁的线程永远等待。死锁产生的四个必要条件Coffman 条件互斥Mutual Exclusion资源只能被一个线程独占。占有并等待Hold and Wait线程已持有至少一个资源并等待获取其他资源。不可剥夺No Preemption资源只能由持有者主动释放。循环等待Circular Wait线程之间形成一条循环等待资源链。3-3 线程拿着锁睡觉这是我们这个代码的另一个问题我们里面的usleep应该删除掉避免锁拿着线程进行睡觉这是非常不合理的所以综合下来我们的程序应该是这样的#includeiostream#includethread#includevector#includestring#includecstdio#includeunistd.hintticket100;pthread_mutex_t lockPTHREAD_MUTEX_INITIALIZER;voidroutine(std::string name){while(1){pthread_mutex_lock(lock);if(ticket0){//说明可以开始抢票ticket--;printf(%s shell ticket,now tickets number:%d\n,name.c_str(),ticket);pthread_mutex_unlock(lock);}else{pthread_mutex_unlock(lock);break;}}return;}intmain(){std::vectorstd::threadthreads;for(inti0;i5;i){std::string namethread-;namestd::to_string(i);threads.emplace_back(routine,name);}for(autothread:threads){thread.join();}return0;}3-4 一个现象我们发现一直是线程2在进行抢票这一段里一直是thread-2在卖票说明这段时间里它反复拿到了 CPU并且每次也都先抢到了那把锁。它先抢到 CPU于是更有机会再次执行到pthread_mutex_lock而锁一旦被它释放它又很快再次抢回来了。所以互斥锁并不能保证公平。4 总结这篇文章从一张神奇的负数车票开始带我们走进了多线程编程中最头疼的问题——竞态条件。当我们用五个线程同时去抢那100张票时本该在票数为0时就停止的程序竟然一路狂奔到了-4。这背后的元凶就是经典的check-then-act race线程A刚判断完票数大于0还没完成减减操作就被线程B抢占了CPU等A回来继续执行时手里的旧情报已经失效了却还要对已经变了的票数再做一次减减。这种对公共资源的并发访问如果不加以保护数据就会像脱缰的野马一样乱套。为了解决这个问题我们引入了互斥锁Mutex这个交通警察。它保证同一时间只有一个线程能进入临界区——也就是访问共享资源的那段代码。加锁和解锁的时机很有讲究锁的范围要刚好覆盖对公共资源的操作但不能太大比如不能把usleep也包进去否则就是拿着锁睡觉白白浪费别人的时间同时每一个分支路径都要记得解锁不然就会触发死锁让其他线程永远等在那里。文章最后也提了一个有趣的现象即便有了锁线程2还是能把票抢光——这说明互斥锁只保证互斥不保证公平谁抢到CPU谁就有机会先拿到锁。总的来说线程互斥是多线程编程的必修课。理解临界区、掌握锁的粒度、警惕死锁的四个必要条件这些基本功打扎实了才能写出既高效又安全的多线程程序。毕竟在这个并发为王的时代让线程们有序竞争比野蛮抢食要靠谱得多。

相关文章:

【c++与Linux进阶】线程篇 -互斥锁

1. 前言: 在我们之前学习的代码种,就是在建造多线程的路上,我们可以看到出现了乱码或者抢占输出,这是为什么呢? 本章将带着这个问题来带你思考: 一个例子先来领略问题的所在。什么是线程互斥.见识互斥锁。…...

深度探索 Gemini CLI:如何实现 Token 消耗的全局自动化统计?

深度探索 Gemini CLI:如何实现 Token 消耗的全局自动化统计? 1. 从 /stats model 说起:单次会话的“极客看板” 如果你是一名 Gemini CLI 的深度用户,一定被它的 /stats model 命令震撼过。输入这个完整指令,Gemini …...

AI+文旅落地实操:巨有科技AI伴游系统架构解析与景区落地案例

在智慧景区数字化转型进程中,导览服务的智能化升级是核心痛点之一——传统真人导游成本高、讲解同质化、离线场景无法适配,而普通AI导览多为固定话术输出,缺乏交互性与个性化,难以满足游客多样化需求。作为文旅数字化领军者&#…...

Qt MQTT部署

1、MQTT源码下载 https://gitcode.com/open-source-toolkit/4b3f0 2、编译源码 (1)解压下载的源码,用QT打开工程文件 (2)构建 --> 执行qmake --> 构建项目(使用Release编译) &#xff0…...

DTD属性详解:从入门到精通

DTD 属性基础概念DTD&#xff08;Document Type Definition&#xff09;中属性的定义用于为元素添加额外信息。属性通过<!ATTLIST>声明&#xff0c;包含元素名称、属性名称、属性类型和默认值。属性声明语法&#xff1a;<!ATTLIST element_name attribute_name attrib…...

Day 3 面试算法练习:二叉树层序遍历

核心思路&#xff1a;利用队列&#xff0c;根左右的顺序循环出队入队时间复杂度&#xff1a;o(n)from collections import dequeclass TreeNode:def __init__(self, val0, leftNone, rightNone):self.val valself.left leftself.right rightdef level_order(root):if root i…...

网安学习笔记|Windows进程、服务与排查手段:从入门到实操,筑牢系统安全基础

作为一名网安初学者&#xff0c;在入门阶段最深刻的感悟是&#xff1a;对Windows系统底层的认知&#xff0c;是做好网络安全的基础。无论是漏洞挖掘、恶意代码分析&#xff0c;还是应急响应、入侵排查&#xff0c;都离不开对进程、服务的理解&#xff0c;更需要熟练掌握系统排查…...

麦橘超然Flux控制台部署全流程:环境准备到图像生成一步到位

麦橘超然Flux控制台部署全流程&#xff1a;环境准备到图像生成一步到位 想体验麦橘超然&#xff08;MajicFLUX&#xff09;模型惊艳的图像生成能力&#xff0c;但被复杂的本地环境配置和显存要求劝退&#xff1f;今天&#xff0c;我将带你从零开始&#xff0c;一步步部署一个基…...

基于TI电赛开发板的L298N电机驱动模块PWM调速移植实战

基于TI电赛开发板的L298N电机驱动模块PWM调速移植实战 最近在准备电赛&#xff0c;很多同学都在为智能小车项目里的电机控制发愁。大家手里都有经典的L298N电机驱动模块&#xff0c;但怎么把它和TI的电赛开发板&#xff08;比如MSP430系列&#xff09;连起来&#xff0c;用PWM实…...

Qwen-Image-2512-Pixel-Art-LoRA 模型v1.0 在网络安全教育中的应用:生成网络攻防场景示意图

Qwen-Image-2512-Pixel-Art-LoRA 模型v1.0 在网络安全教育中的应用&#xff1a;生成网络攻防场景示意图 你有没有过这样的经历&#xff1f;在给团队做网络安全培训&#xff0c;或者给学生讲解网络攻击原理时&#xff0c;费尽口舌描述了半天&#xff0c;底下的人还是一脸茫然。…...

互动艺术装置创意实现:cv_resnet101_face-detection_cvpr22papermogface驱动实时人脸特效

互动艺术装置创意实现&#xff1a;用实时人脸检测驱动你的艺术灵感 你有没有想过&#xff0c;站在一面看似普通的镜子或屏幕前&#xff0c;你的脸会瞬间变成一片流动的星空、一朵绽放的花&#xff0c;或者被一群跟随你表情舞动的粒子所包围&#xff1f;这不是科幻电影&#xf…...

Neeshck-Z-lmage_LYX_v2实战体验:一键切换LoRA风格,轻松生成精美画作

Neeshck-Z-lmage_LYX_v2实战体验&#xff1a;一键切换LoRA风格&#xff0c;轻松生成精美画作 你是否曾对AI绘画跃跃欲试&#xff0c;却被复杂的模型部署、繁琐的权重切换和令人望而却步的显存需求劝退&#xff1f;今天&#xff0c;我想分享一个让我彻底摆脱这些困扰的发现——…...

AI原生应用领域函数调用的版本管理与更新策略

AI原生应用领域函数调用的版本管理与更新策略关键词&#xff1a;AI原生应用、函数调用、版本管理、更新策略、技术架构摘要&#xff1a;本文聚焦于AI原生应用领域中函数调用的版本管理与更新策略。首先介绍了相关背景知识&#xff0c;接着深入解释函数调用、版本管理和更新策略…...

新手福音:借快马一键生成openclaw101登录页,轻松理解前后端交互

作为一名刚刚踏入编程世界的新手&#xff0c;我最近对如何制作一个网站登录页面特别感兴趣。这听起来像是每个网站都有的基础功能&#xff0c;但真要自己动手&#xff0c;却发现涉及前端、后端、数据交互等一大堆陌生的概念&#xff0c;让人有点无从下手。幸运的是&#xff0c;…...

万豪酒店的“疯狂三月”广告突显了酒店在赛前仪式中的关键作用

万豪酒店集团近期针对美国大学篮球盛事“疯狂三月&#xff08;March Madness&#xff09;”&#xff08;NCAA全国锦标赛&#xff09;推出了一场名为“赛事日入住&#xff08;Where Gameday Checks In&#xff09;”的全新整合营销活动。该活动旨在展现酒店服务业在大型体育赛事…...

华硕主板风扇控制异常完全解决方案:从诊断到优化的系统方法

华硕主板风扇控制异常完全解决方案&#xff1a;从诊断到优化的系统方法 【免费下载链接】FanControl.Releases This is the release repository for Fan Control, a highly customizable fan controlling software for Windows. 项目地址: https://gitcode.com/GitHub_Trendi…...

Z-Image-Turbo-辉夜巫女多场景实战:同人展海报、社团Banner、推特封面制作

Z-Image-Turbo-辉夜巫女多场景实战&#xff1a;同人展海报、社团Banner、推特封面制作 1. 引言&#xff1a;当二次元创作遇上AI生产力 如果你是动漫同人创作者、社团运营者&#xff0c;或者只是一个喜欢辉夜巫女这个角色的爱好者&#xff0c;你肯定遇到过这样的烦恼&#xff…...

SPIRAN ART SUMMONER创新研究:基于ControlNet的精确构图控制

SPIRAN ART SUMMONER创新研究&#xff1a;基于ControlNet的精确构图控制 探索AI绘画的精准控制新境界&#xff0c;让创意不再受限于随机生成 1. 核心能力概览 SPIRAN ART SUMMONER结合ControlNet技术&#xff0c;为AI图像生成带来了前所未有的精确控制能力。传统的文生图模型虽…...

SenseVoice-Small模型部署避坑指南:解决403 Forbidden等常见网络与权限问题

SenseVoice-Small模型部署避坑指南&#xff1a;解决403 Forbidden等常见网络与权限问题 部署AI模型&#xff0c;尤其是从开源社区拉取模型时&#xff0c;最让人头疼的不是代码逻辑&#xff0c;而是那些看似玄学的环境问题。你照着教程一步步来&#xff0c;结果卡在了一个“403…...

Phi-3-Mini-128K企业级部署:支持Docker Compose编排+GPU资源隔离

Phi-3-Mini-128K企业级部署&#xff1a;支持Docker Compose编排GPU资源隔离 想体验微软最新的轻量级大模型Phi-3&#xff0c;但被复杂的部署流程和显存要求劝退&#xff1f;今天分享一个开箱即用的解决方案——一个基于Phi-3-mini-128k-instruct模型开发的本地对话工具。它不仅…...

光伏储能基于VSG虚拟同步发电机控制的并网仿真模型搭建与解析

光伏储能基于VSG虚拟同步发电机控制的并网仿真模型 基于Matlab/Simulink仿真平台 储能为buck_boost电路(双向DC/DC变换) 光伏为boost电路 主电路采用三相全桥PWM逆变器 1.仿真均能正常运行&#xff0c;能够准确跟踪对应参考值 2.直流母线电压设置为700V 3.储能部分采用基于PI控…...

LiuJuan20260223Zimage在AIGC内容生成中的创新应用

LiuJuan20260223Zimage在AIGC内容生成中的创新应用 探索AI如何重新定义内容创作的边界 最近试用了一款新的AI图像生成工具LiuJuan20260223Zimage&#xff0c;说实话&#xff0c;效果让我有点惊讶。不是那种"哦&#xff0c;又一个AI工具"的平淡反应&#xff0c;而是真…...

在快马平台用AI快速生成LaTeX学术论文原型,十分钟搞定排版

最近在准备一篇学术会议论文&#xff0c;时间紧任务重&#xff0c;最头疼的就是初期的排版和原型验证。用Word吧&#xff0c;复杂的公式和参考文献格式调整起来太费劲&#xff1b;在本地装LaTeX环境吧&#xff0c;又得折腾一阵。后来发现&#xff0c;直接在InsCode(快马)平台上…...

ESP32复刻诺基亚功能机:嵌入式手持终端全栈设计

1. 项目概述复刻经典功能机并非怀旧情怀的简单投射&#xff0c;而是一次面向嵌入式系统工程实践的完整闭环训练。本项目以Nokia 1110为物理载体与交互范式蓝本&#xff0c;采用ESP32-WROOM-32作为主控平台&#xff0c;构建了一台具备现代嵌入式能力的微型手持终端。其设计目标明…...

医疗数据共享推动糖尿病研究突破:Awesome-CGM开源生态的价值与实践

医疗数据共享推动糖尿病研究突破&#xff1a;Awesome-CGM开源生态的价值与实践 【免费下载链接】Awesome-CGM List of CGM datasets 项目地址: https://gitcode.com/gh_mirrors/aw/Awesome-CGM 当一位内分泌科医生尝试验证新型胰岛素给药算法的临床效果时&#xff0c;当…...

Guohua Diffusion 虚拟角色设计:从文本描述到三视图的完整流程

Guohua Diffusion 虚拟角色设计&#xff1a;从文本描述到三视图的完整流程 最近在尝试用AI做虚拟角色设计&#xff0c;发现Guohua Diffusion在这方面表现挺让人惊喜的。你可能也遇到过类似情况&#xff1a;脑子里有个很酷的角色形象&#xff0c;但自己画不出来&#xff0c;或者…...

哪些行业用动态代理ip?哪些行业用静态代理IP?怎样区分动态ip和静态ip?(互联网人必码·实用长文)

做互联网运维代理IP服务6年&#xff0c;每天被同行、客户追问最多的问题&#xff1a;“我们做爬虫&#xff0c;该用动态代理还是静态代理&#xff1f;”“跨境电商选代理IP&#xff0c;静态和动态到底差在哪&#xff1f;”“普通人怎么快速分清这两种代理IP&#xff0c;不被服务…...

Whatsapp协议号六段提取

做whatsapp应该都听过协议号&#xff0c;这是一种非正常形式注册的一种账号&#xff1b;通常如下格式&#xff1a;账号,公钥,私钥,消息公钥,消息私钥,号码ID形如&#xff1a;52181287741111,S3yLJQW1czAkEd77MmxxxxkgLFqPULnLI8WKtrTikWw,EE45xxxxxGKfRMld7FbV4ovAScbItifWPeoD…...

ESP32智能风扇系统设计:嵌入式软硬协同实践

1. 项目概述AbenFanPro 是一款面向嵌入式爱好者与硬件创客的多功能智能风扇系统&#xff0c;其设计目标并非仅限于空气动力学功能的实现&#xff0c;而是将传统家电重构为可交互、可编程、可扩展的嵌入式计算平台。该系统在保持基础送风功能的前提下&#xff0c;集成了环境感知…...

业余探空火箭飞控系统:鸭式布局与模块化设计实践

1. 项目概述“有控探空火箭-飞控”是一个面向业余火箭实践的模块化飞控系统工程实现&#xff0c;其核心目标是构建一套具备姿态感知、闭环控制、安全回收能力且可复现、可扩展的低成本探空火箭技术验证平台。该项目并非以商业发射或高精度科研载荷投送为最终导向&#xff0c;而…...