【Linux】线程互斥

article/2025/6/7 22:51:55

📝前言:
这篇文章我们来讲讲Linux——线程互斥

🎬个人简介:努力学习ing
📋个人专栏:Linux
🎀CSDN主页 愚润求学
🌄其他专栏:C++学习笔记,C语言入门基础,python入门基础,C++刷题专栏


这里写目录标题

  • 一,什么是线程互斥
    • 1. 背景概念回顾
    • 2. 没有互斥的代码示例
      • 2.1 示例
      • 2.2 解释现象
        • 理解代码与指令
        • 解释为什么出现负票
  • 二,互斥量mutex
    • 接口(pthread库的)
      • 1. 初始化
      • 2. 销毁
      • 3. 加锁和解锁
    • 示例(解决抢票问题)
  • 三,mutex原理
    • 1. 硬件实现
    • 2. 软件实现
  • 四,mutex封装

一,什么是线程互斥

1. 背景概念回顾

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

2. 没有互斥的代码示例

2.1 示例

当线程之间,并发的操作共享变量,且没有互斥量mutex(锁)保护的时候,就可能出现数据不一致问题。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <iostream>
#include <vector>int tickets = 1000; // 总用一千张票void* buyticket(void* args)
{std::string name = static_cast<char*>(args);while(true){if(tickets > 0)std::cout << name << " buy ticket: " << tickets-- << std::endl;elsebreak;}return nullptr;
}int main()
{pthread_t threads[5];for(int i = 1; i <= 5; i++){char name[64];snprintf(name, sizeof(name), "thread-%d", i);pthread_create(&threads[i - 1], nullptr, buyticket, name);}for(int i = 1; i <= 5; i++){pthread_join(threads[i - 1], nullptr);}return 0;
}

运行结果:
在这里插入图片描述
很明显,票数减多了。为什么呢?

2.2 解释现象

理解代码与指令

我们的tickets--操作,变成汇编其实是三条指令。
在这里插入图片描述
在以上三条指令中间,线程都有可能被切换,当线程被切换,线程会把 ebx 和 PC 寄存器里的上下文数据保存到自己的PCB里面,然后离开。(即下一个线程可覆盖原来寄存器里的数据)

比如,原始票数为1000,当进程 A 执行完 1,2步(此时ticket应该为999),结果刚好被切换了, 进程 B 从第一步开始执行,因为进程 A 的999没有写回内存,所以进程 B 载入的也是1000,这就导致了内存不一致。

解释为什么出现负票

在这里插入图片描述

  • 问题在于if判断,假如这5个线程在票数为1的时候,依次进行了if判断,而且刚好都在if判断完以后就立马切换成下一个线程判断,则所有线程都是ticket == 1的时候通过的if判断。
  • 所有进程都会进入if语句去执行buy ticket的操作。
  • 然后这时候执行buy ticket的操作是串行的(一个一个线程执行),比如,当线程1执行完(ticket == 1载入,计算得0,把0写回)
  • 线程2因为已经过了if所以也要执行,这时候线程2载入的ticket == 0,经过一系列操作ticket就会变成负数

二,互斥量mutex

要解决上面的问题,我们就要使用互斥量mutex(也是一个变量,也存储值,也存储在内存里面),也叫做
在这里插入图片描述

接口(pthread库的)

1. 初始化

静态分配(全局初始化),会自动销毁

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
  • pthread_mutex_tmutex的类型

动态分配,谁定义谁销毁

int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr)
  • mutex:指向要初始化的锁(已经分配好内存的)
  • attr:传入NULL,表示使用默认属性的锁

2. 销毁

 int pthread_mutex_destroy(pthread_mutex_t *mutex)
  • 不要销毁⼀个已经加锁的互斥量
  • 已经销毁的互斥量,要确保后⾯不会有线程再尝试加锁

3. 加锁和解锁

一般,如果多个线程访问同一个临界区,则这多个线程竞争的应该是同一把锁。

加锁

int pthread_mutex_lock(pthread_mutex_t *mutex);

如果锁被占用(或者没有竞争过其他执行流),加锁不成功就会阻塞(执⾏流被挂起)

解锁

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

当然,C++也有专门的一套锁的方法(封装的pthread),接口更简单,方便用户使用

示例(解决抢票问题)

    while (true){pthread_mutex_lock(&mutex);if (tickets > 0){std::cout << name << " buy ticket: " << tickets-- << std::endl;pthread_mutex_unlock(&mutex);}else{pthread_mutex_unlock(&mutex);break;}}

输出结果:
在这里插入图片描述
为什么全是线程 5 抢的票,这是因为,当线程 5 解锁以后,马上又进入下一个循环申请锁了,而其他线程还要“唤醒”等等操作,线程 5 最近,所以线程 5 一直竞争成功。这也导致了其他线程的饥饿问题(下一篇文章会讲述)

三,mutex原理

要解决的问题就是:当一个线程竞争到锁以后,访问临界区的时候,其他线程不能进入临界区。

1. 硬件实现

在一个线程竞争到锁以后,关闭时钟中断,让线程无法切换,这样其他线程就不会进入临界区了

2. 软件实现

通过将内存中mutex的值与当前竞争到锁的线程的exb的值交换,使得,只有当前线程的硬件上下文里面的mutex1

以抢票的代码为例

  • 首先,mutex是全局变量,mutex在内存中原来存储的值是1
  • 对于每个进程,申请锁,并判断能不能进入临界区的汇编分为两步
    • 第一步(申请锁):把 0 传到 寄存器%al,然后把%al的内容和内存中mutex的内容做交换
    • 第二步(判断):如果al的内容 > 0就代表当前线程申请到锁了,进入临界区,否则挂起等待

在这里插入图片描述

  • 如果线程 A 竞争到锁了,原来mutex的值是1,交换%al和内存mutex的值,线程 A 的%al中存储的就是 1 了,mutex内存中就是 0了。就算此时线程 A 被切换,线程 A 也能带着 %al中的 1这个上下文被切换。
  • 此时,其他 线程再来申请锁,因为内存中的mutex的值已经是 0了,所以无论怎么交换,得到的都是0,过不了判断,无法进入临界区
  • 对于线程 A ,过了判断后进入临界区,%al寄存器的值被覆盖了也没事,恢复锁的时候,直接往内存的mutex写回 1
  • 然后唤醒其他等待mutex的线程,再竞争

在这里插入图片描述

四,mutex封装

我们像语言层一样,封装系统的mutex

#pragma once
#include <pthread.h>class Mutex
{
public:Mutex(){pthread_mutex_init(&_mutex, nullptr);}~Mutex(){pthread_mutex_destroy(&_mutex);}void Lock(){pthread_mutex_lock(&_mutex);}void Unlock(){pthread_mutex_unlock(&_mutex);}
private:pthread_mutex_t _mutex;
};

🌈我的分享也就到此结束啦🌈
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
📢公主,王子:点赞👍→收藏⭐→关注🔍
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!


http://www.hkcw.cn/article/VdJugoLbAA.shtml

相关文章

「OC」初识runloop

「OC」初识runloop 简介 iOS中的RunLoop&#xff08;运行循环&#xff09;是事件处理的核心机制&#xff0c;负责管理线程的生命周期、事件调度及资源优化。其核心作用是通过循环处理输入事件、定时器任务和观察者回调&#xff0c;保持线程活跃且高效运行。 runloop的作用 R…

python学习打卡day43

DAY 43 复习日 作业&#xff1a; kaggle找到一个图像数据集&#xff0c;用cnn网络进行训练并且用grad-cam做可视化 浙大疏锦行 数据集使用猫狗数据集&#xff0c;训练集中包含猫图像4000张、狗图像4005张。测试集包含猫图像1012张&#xff0c;狗图像1013张。以下是数据集的下…

【AI学习从零至壹】基于深度学习的⽂本分类任务

基于深度学习的⽂本分类任务 文本分类任务的实现思路⽂本预处理文本分词Jieba分词文本分词器SentencePiece训练步骤⽂本结构化转换 语料转换为训练样本 文本分类任务的实现思路 ⽂本分类就是⼀个将⽂本分配到预定义类别的⼀个过程 整体流程包括&#xff1a; ⽂本语料的获取和…

sourcetree中的mercurial有什么用

1、安装SourceTree的过程中&#xff0c;有一个选项就是mercurial&#xff0c;&#xff0c;一直没搞明白他是干什么用的&#xff0c;直到今天 2、ai登场 3、总结 此软件无用&#xff0c;不需要安装

【Linux】linux基础指令

目录 管理用户相关useraddpaaswduserdelLinux中的用户文件结构 ls-aLinux目录中的.和..是什么&#xff1f; -l-d-FLinux指令使用多个选项 pwdcd绝对路径与相对路径 touchmkdir-p rmdir-p rm-r-i-f mancpmvecho输出重定向和追加重定向 cat-b-n-s moreless-N-i headtail管道文件搭…

Linux中shell介绍

一、脚本实践 脚本示例1 -- 直接编辑并创建一个文件 vim bak.sh-- 写入下面这句话 # 获取ip地址信息 ifconfig ens33 | grep -w inet | awk {print $2} | xargs echo "IP: "-- 运行bak文件 bash bak.sh或者-- 添加可执行权限 chmod ax bak.sh./bak.sh或者source ba…

【智能制造】精读57页智慧工厂MES 项目解决方案【附全文阅读】

本文概述了智慧工厂MES项目解决方案在工业4.0背景下的整体框架与应用。智慧工厂以企业管理运营中心为核心&#xff0c;融合战略绩效、集团管控、决策分析及大数据分析平台&#xff0c;实现C2M&#xff08;Consumer to Manufacturer&#xff09;个性化订单处理。通过信息化系统平…

Stable Diffusion 技术原理解析与代码实践

1. 引言 Stable Diffusion 是由 Stability AI 开发的开源文本到图像生成模型,自 2022 年发布以来在创意产业和研究领域引起了广泛关注。它基于潜在扩散模型架构,能够根据文本描述生成高质量的图像内容,为艺术创作、设计和内容生成提供了强大工具。 2. 技术原理详解 2.1 扩…

Cursor + Claude 4:海外工具网站开发变现实战案例

项目背景 在全球数字化浪潮中&#xff0c;海外工具网站市场蕴含着巨大的商业机会。本文将详细介绍如何使用Cursor编辑器结合Claude 4 AI助手&#xff0c;开发一个面向海外用户的多功能工具网站"ToolBox Pro"&#xff0c;并通过多元化策略实现有效变现。该项目在6个月…

AI 赋能名片设计:告别模板化,创造独特视觉风格

在商务社交与个人品牌传播中&#xff0c;名片是传递信息的重要载体&#xff0c;但千篇一律的模板设计往往让印象大打折扣。智能设计工具的出现&#xff0c;正以智能排版、创意生成和高效迭代的优势&#xff0c;颠覆传统名片设计范式。本文将推荐创客贴、Canva、Fotor 懒设计等多…

MCP:让AI工具协作变得像聊天一样简单 [特殊字符]

想象一下,你正在处理一个项目,需要从A平台查看团队讨论,从B平台获取客户信息,还要在GitHub上检查代码进度。传统做法是什么?打开三个不同的网页,在各个平台间来回切换,复制粘贴数据,最后还可能因为信息分散而遗漏重要细节。 听起来很熟悉?这正是当前工作流程的痛点所…

h5的aliplayer-min.js 加密视频会走到debugger

h5的aliplayer-min.js 如果 https://g.alicdn.com/apsara-media-box/imp-web-player/2.19.0/aliplayer-min.js走加密视频的话会有debugger 更换aliplayer-min.js版本解决了 https://g.alicdn.com/apsara-media-box/imp-web-player/2.25.1/aliplayer-min.js 对应css&#xff1a…

AgenticSeek:您的本地AI智能大脑,免费且私密

还在为昂贵的AI智能体订阅费望而却步吗&#xff1f;还在担心将敏感数据交由第三方处理的隐私风险吗&#xff1f;现在&#xff0c;一款名为AgenticSeek的AI工具横空出世&#xff0c;它承诺提供一个完全免费、100%本地化运行、且功能强大的AI智能体解决方案&#xff0c;旨在成为付…

活动选择问题一文详解

活动选择问题一文详解 一、活动选择问题描述1.1 问题定义1.2 示例说明 二、贪心算法求解策略2.1 贪心思想2.2 策略证明2.3 算法步骤 三、代码实现3.1 Python 实现3.2 C 实现3.3 Java 实现 四、复杂度分析4.1 时间复杂度4.2 空间复杂度 五、应用拓展5.1 资源分配5.2 任务调度优化…

xmake的简易学习

文章目录 1. xmake是什么2. 一个可执行程序3. 一个库文件4. 遍历文件用法5. 第三方库3.1 系统安装库3.2 独立库 6. 后续 由于前一篇博客的最后说要做一些rknn的优化&#xff0c;其实这个工作很早就完成了&#xff0c;但是我是使用 xmake这个来做我的工程的构建的&#xff0c;不…

【网络安全 | 信息收集】灯塔(资产收集工具)安装教程

文章目录 简介安装教程1.创建文件2.执行命令3.运行程序简介 ARL(Asset Reconnaissance Lighthouse)资产侦察灯塔系统,旨在快速侦察与目标关联的互联网资产,构建基础资产信息库。 协助甲方安全团队或者渗透测试人员有效侦察和检索资产,发现存在的薄弱点和攻击面。 其特性如…

TCP小结

1. 核心特性 面向连接&#xff1a;通过三次握手建立连接&#xff0c;四次挥手终止连接&#xff0c;确保通信双方状态同步。 TCP连接建立的3次握手 抓包&#xff1a; client发出连接请求&#xff1b; server回应client请求&#xff0c;并且同步发送syn连接&#xff1b; clien…

Ansys Zemax | 手机镜头设计 - 第 3 部分:使用 STAR 模块和 ZOS-API 进行 STOP 分析

附件下载 联系工作人员获取附件 该系列文章将讨论智能手机镜头模组设计的挑战&#xff0c;从概念、设计到制造和结构变形的分析。本文是四部分系列的第三部分&#xff0c;它涵盖了使用 Ansys Zemax OpticStudio Enterprise 版本提供的 STAR 技术对智能手机镜头进行自动的结构…

【Redis】set 类型

set 一. set 类型介绍二. set 命令sadd、smembers、sismemberscard、spop、srandmembersmove、srem集合间操作交集&#xff1a;sinter、sinterstore并集&#xff1a;sunion、sunionstore差集&#xff1a;sdiff、sdiffstore 三. set 命令小结四. set 内部编码方式五. set 使用场…

006网上订餐系统技术解析:打造高效便捷的餐饮服务平台

网上订餐系统技术解析&#xff1a;打造高效便捷的餐饮服务平台 在数字化生活方式普及的当下&#xff0c;网上订餐系统成为连接餐饮商家与消费者的重要桥梁。该系统以菜品分类、订单管理等模块为核心&#xff0c;通过前台展示与后台录入的分工协作&#xff0c;为管理员和会员提…