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

Linux_线程互斥

互斥的相关概念

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

一个不加保护的Demo

这里使用多个线程共同执行上面的方法,代码很简单。但是运行结果怎么出现了负数?等于0不就直接break了吗?

原因有以下两点

  • 代码if(XXXX)不是原子操作,ticketnum--也不是原子操作
  • 所有的线程在尽可能多的进行调度切换执行 --- 线程或者进程什么时候会切换?
    • a.时间片耗尽
    • b.更高优先级的进程要调度
    • c.通过sleep,从内核返回用户时,会进行时间片是否到达的检测,进而导致切换

当我们执行上述代码时,每个线程都要这样执行上面的逻辑,但cpu的寄存器只有一套,但是寄存器中的数据有多套,且数据为线程私有。由于ticketnum--操作不是原子的(即,将ticketnum的值移动到CPU,CPU做运算,再将结果写回内存。共三步)。当一个线程正走到以上逻辑的第二步时,正准备判断,此时这个线程被切换了,一旦被切换,当前线程在寄存器中数据都会保存下来,等在被切回来的时候,再恢复!

        当票数为1时,a线程会做判断,符合逻辑进入if,走到usleep语句;此时b线程也进来了,a将寄存器中的数据带走,此时b线程见到的票数也是1,b线程也符合逻辑,进入if,也会走到usleep;同样的c和d线程都会做以上线程的动作,都会进入if。当a过了usleep时间,会执行--操作(1.重读数据2.--数据3.写回数据),此时票数为0了,同样的b,c,d线程也会做--,因为它们已经进入了if中。最后就导致票数为-2的情况了。

互斥量mutex

在Linux中互斥量就是锁

要解决上述多线程并发引起的安全问题,我们只需在进入临界区之前加上一把锁,就可以完美解决。

 互斥量(锁)的相关接口

  • pthread_mutex_init: 初始化互斥锁。
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;// 使用宏值初始化(全局)

    mutex:指向要初始化的互斥锁对象的指针。

    attr:指定互斥锁属性的对象,如果传递NULL,则使用默认的互斥锁属性。

    pthread_mutex_init 函数若调用成功,会返回 0。若发生错误,会返回一个非零的错误码。

    • pthread_mutex_destroy: 销毁互斥锁。
    int pthread_mutex_destroy(pthread_mutex_t *mutex);
    
    • pthread_mutex_lock: 锁定互斥锁
    int pthread_mutex_lock(pthread_mutex_t *mutex);
    
    • pthread_mutex_unlock: 解锁互斥锁。
     int pthread_mutex_unlock(pthread_mutex_t *mutex);
    

    锁接口的使用

    全局锁

    // 定义一个全局锁
    pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
    int ticketnum = 10000; // 共享资源,临界资源void Ticket()
    {while (true){pthread_mutex_lock(&lock); // 加锁if (ticketnum > 0){usleep(1000);printf("get a new ticket, id: %d\n", ticketnum--);pthread_mutex_unlock(&lock);     // 解锁}else{pthread_mutex_unlock(&lock);     // 解锁break;}}
    }
    int main()
    {// 创建多线程的逻辑,调用Tichetreturn 0;
    }

     局部锁

    使用ThreadData接收参数,包括锁的接收,保证每一个线程都能看到同一把锁

    #include <iostream>
    #include <string>
    #include <pthread.h>
    #include <functional>
    #include <sys/types.h>
    #include <unistd.h>namespace ThreadModule
    {// 要传递的参数struct ThreadData{ThreadData(const std::string &name, pthread_mutex_t *lock_ptr): _name(name), _lock_ptr(lock_ptr){}std::string _name;pthread_mutex_t *_lock_ptr;};// 执行任务的方法using func_t = std::function<void(ThreadData*)>;// 线程状态-枚举enum class TSTATUS{NEW,RUNNING,STOP};class Thread{private:// 成员方法,具备this指针,置为static之后就不具备this指针了static void *Routine(void *args){// t就拿到了this指针Thread *t = static_cast<Thread *>(args);t->_status = TSTATUS::RUNNING;t->_func(t->_td); // 就可以执行相应的类内方法了return nullptr;}public:// 线程要执行的方法直接传进来Thread(const std::string &name, func_t func, ThreadData* td): _name(name), _func(func), _td(td), _status(TSTATUS::NEW), _joinable(true){}bool Start(){if (_status != TSTATUS::RUNNING){int n = ::pthread_create(&_tid, nullptr, Routine, this); // 将this指针通过参数传过去if (n != 0)return false;return true;}return false;}bool Stop(){if (_status == TSTATUS::RUNNING){int n = ::pthread_cancel(_tid);if (n != 0)return false;_status = TSTATUS::STOP;return true;}return false;}bool Join(){if (_joinable){int n = ::pthread_join(_tid, nullptr);if (n != 0)return false;_status = TSTATUS::STOP;return true;}return false;}std::string Name() { return _name; }~Thread() {}private:std::string _name; // 线程名字pthread_t _tid;    // 线程idbool _joinable;    // 是否是分离状态,默认不是func_t _func;      // 线程未来要执行的方法TSTATUS _status;   // 线程状态ThreadData* _td;   // 要传递的参数};
    }
    

    让每个线程都获取局部锁的地址,在每个线程在执行抢票逻辑的时候,将锁的地址传给加锁函数,就能实现局部加锁了。 

    #include "Thread.hpp"
    #include <vector>int ticketnum = 10000;
    void Ticket(ThreadModule::ThreadData *td)
    {while(true){pthread_mutex_lock(td->_lock_ptr);      // 加锁if(ticketnum > 0){// 抢票printf("get a new ticket, who get it: %s, id: %d\n", td->_name.c_str(), ticketnum--);pthread_mutex_unlock(td->_lock_ptr);// 解锁}else{pthread_mutex_unlock(td->_lock_ptr);// 解锁break;}}
    }
    #define NUM 4
    int main()
    {// 创建局部锁pthread_mutex_t mutex;pthread_mutex_init(&mutex, nullptr);// 创建线程对象std::vector<ThreadModule::Thread> threads;for(int i = 0;i < NUM; i++){std::string name = "thread-" + std::to_string(i+1);// 把锁的地址给到td对象ThreadModule::ThreadData *td = new ThreadModule::ThreadData(name, &mutex);// 之后在将td给到Threadthreads.emplace_back(name, Ticket, td);}// 启动线程for(int i = 0; i< NUM;i++)threads[i].Start();// 等待线程for(int i = 0; i< NUM;i++)threads[i].Join();// 释放锁pthread_mutex_destroy(&mutex);  return 0;
    }

    锁的相关问题

    1. 锁本身是全局的,那么锁也是共享资源!谁保证锁的安全?

            pthread_mutex:加锁和解锁被设计成为原子的了

    2. 如何看待锁呢?二元信号量就是锁!

            2.1 加锁本质就是对资源展开预订!

            2.2 整体使用资源!!

    3. 如果申请锁的时候,锁被别人已经拿走了,怎么办?

            其他线程要进行阻塞等待

    4. 线程在访问临界区代码的时候,可以不可以切换?可以切换!!

            4.1 我被切走的时候,别人能进来吗?不能!因为我是抱着锁,被切换的!临界区的代码就是被串行的!这也是加锁效率低的原因!也体现了原子性(要么不做,要么做完)!

    锁是如何实现的

    现在大家已经意识到单纯的 i++ 或者 ++i 都不是原子的,有可能会有数据一致性问题。
    在内核中,为了实现互斥锁操作,大多数体系结构都提供了swap或exchange指令,该指令的作用是把寄存器和内存单元的数据相交换,由于只有一条指令,保证了原子性,即使是多处理器平台,访问内存的 总线周期也有先后,一个处理器上的交换指令执行时,另一个处理器的交换指令只能等待总线周期。 

    将%al看成一个寄存器,把 0 movb到 %al中,xchgb将内存中的变量与寄存器中的做了直接的交换,不需要中间变量。(我们假定mutex一开始的数据是:1表示锁没有被申请;0表示锁被申请了)。线程执行判断,如果%al中的内容>0,则申请锁成功然后返回,否则挂起等待,等待完成被唤醒,goto lock重新申请锁。

    1. CPU的寄存器只有一套,被所有的线程共享。但是寄存器中的数据,属于执行流上下文,属于执行流私有的数据!
    2. CUP在执行代码的时候,一定要有对应的执行载体 -- 线程&&进程。
    3. 数据在内存中,是被所有线程所共享的

    结论:把数据从内存移动到寄存器,本质是把数据从共享,变成线程私有!


    重新理解加锁

    当线程A执行第一行代码时,此时%al寄存器中为0,内存mutex中为1(图1);执行第二条代码时内存中mutex中的数据与%al进行交换,变为%al中值为1,mutex的值为0(图2);我们假设线程A执行第三行代码的时候被切换走,线程A会保存自身的上下文,带走%al中的数据,此时线程A处在第三行。

            这时线程B来了,并且开始走第一行和第二行代码,由于内存中mutex的值为0(还是处于图2的状态),交换之后%al的值还是0。所以当线程B执行到第3行代码的时候只能跳到第6行,进行挂起等待。

            线程B被挂起,线程A被重新切回,并恢复上文数据,从第三行开始执行,进入if,调用接口pthread_mutex_lock,return 0表示加锁成功,进入临界区。所以此时线程A被称为:申请锁成功。在上面代码中,加锁就是执行第二行代码:xchgb,只有一条汇编代码,交换不是拷贝,只有一个“1”,持有1的,就表示持有锁!

            当线程A执行完临界区的代码后,进行解锁,执行第八行代码,将自身持有的“1”movb到内存中(这样就回到了图1的状态),接着唤醒正在等待mutex的线程B,线程B被唤醒后,执行第七行代码,继续goto lock重新申请锁。

    相关文章:

    Linux_线程互斥

    互斥的相关概念 共享资源&#xff1a;指多个进程或线程可以共同访问和操作的资源临界资源&#xff1a;被保护的共享资源就叫做临界资源临界区&#xff1a;每个线程内部&#xff0c;访问临界资源的代码&#xff0c;就叫做临界区互斥&#xff1a;任何时刻&#xff0c;互斥保证有…...

    基于 NodeJs 一个后端接口的创建过程及其规范 -- 【elpis全栈项目】

    基于 NodeJs 一个后端接口的创建过程及其规范 一个接口的诞生&#xff1a; #mermaid-svg-46HXZKI3fdnO0rKV {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-46HXZKI3fdnO0rKV .error-icon{fill:#552222;}#mermaid-sv…...

    企业知识库提升企业核心竞争力促进团队协作和知识分享

    内容概要 在快速发展的数字化时代&#xff0c;企业知识库的构建与运用变得愈发重要。其重要性不仅体现在信息的集中管理上&#xff0c;更在于推动企业整体竞争力的提升。一个高效的知识库可以作为团队合作的重要平台&#xff0c;促进不同部门之间的信息交流与协作&#xff0c;…...

    C++ unordered_map和unordered_set的使用,哈希表的实现

    文章目录 unordered_map&#xff0c;unorder_set和map &#xff0c;set的差异哈希表的实现概念直接定址法哈希冲突哈希冲突举个例子 负载因子将关键字转为整数哈希函数除法散列法/除留余数法 哈希冲突的解决方法开放定址法线性探测二次探测 开放定址法代码实现 哈希表的代码 un…...

    games101-作业3

    由于此次试验需要加载模型&#xff0c;涉及到本地环节&#xff0c;如果是windows系统&#xff0c;需要对main函数中的路径稍作改变&#xff1a; 这么写需要&#xff1a; #include "windows.h" 该段代码&#xff1a; #include "windows.h" int main(int ar…...

    【Block总结】高效多尺度注意力EMA,超越SE、CBAM、SA、CA等注意力|即插即用

    论文信息 标题: Efficient Multi-Scale Attention Module with Cross-Spatial Learning 作者: Daliang Ouyang, Su He, Guozhong Zhang, Mingzhu Luo, Huaiyong Guo, Jian Zhan, Zhijie Huang 论文链接: https://arxiv.org/pdf/2305.13563v2 GitHub链接: https://github.co…...

    Pwn 入门核心工具和命令大全

    一、调试工具&#xff08;GDB 及其插件&#xff09; GDB 启动调试&#xff1a;gdb ./binary 运行程序&#xff1a;run 或 r 设置断点&#xff1a;break *0x地址 或 b 函数名 查看寄存器&#xff1a;info registers 查看内存&#xff1a;x/10wx 0x地址 &#xff08;查看 10 个 …...

    探索AI(chatgpt、文心一言、kimi等)提示词的奥秘

    大家好&#xff0c;我是老六哥&#xff0c;我正在共享使用AI提高工作效率的技巧。欢迎关注我&#xff0c;共同提高使用AI的技能&#xff0c;让AI成功你的个人助理。 "AI提示词究竟是什么&#xff1f;" 这是许多初学者在接触AI时的共同疑问。 "我阅读了大量关于…...

    利用飞书机器人进行 - ArXiv自动化检索推荐

    相关作者的Github仓库 ArXivToday-Lark 使用教程 Step1 新建机器人 根据飞书官方机器人使用手册&#xff0c;新建自定义机器人&#xff0c;并记录好webhook地址&#xff0c;后续将在配置文件中更新该地址。 可以先完成到后续步骤之前&#xff0c;后续的步骤与安全相关&…...

    小白爬虫冒险之反“反爬”:无限debugger、禁用开发者工具、干扰控制台...(持续更新)

    背景浅谈 小白踏足JS逆向领域也有一年了&#xff0c;对于逆向这个需求呢主要要求就是让我们去破解**“反爬机制”**&#xff0c;即反“反爬”&#xff0c;脚本处理层面一般都是decipher网站对request设置的cipher&#xff0c;比如破解一个DES/AES加密拿到key。这篇文章先不去谈…...

    Ubuntu中MySQL安装-02

    服务器端安装 安装服务器端&#xff1a;在终端中输入如下命令&#xff0c;回车后&#xff0c;然后按照提示输入 sudo apt-get install mysql-server 当前使用的ubuntu镜像中已经安装好了mysql服务器端&#xff0c;无需再安装&#xff0c;并且设置成了开机自启动服务器用于接…...

    大数据相关职位介绍之一(数据分析,数据开发,数据产品经理,数据运营)

    大数据相关职位介绍之一 随着大数据、人工智能&#xff08;AI&#xff09;和机器学习的快速发展&#xff0c;数据分析与管理已经成为各行各业的重要组成部分。从互联网公司到传统行业的数字转型&#xff0c;数据相关职位在中国日益成为推动企业创新和提升竞争力的关键力量。以…...

    使用DeepSeek API生成Markdown文件

    DeepSeek技术应用与代码实现 一、DeepSeek简介 DeepSeek是一款强大的人工智能写作助手&#xff0c;能够根据用户输入的提示&#xff08;Prompt&#xff09;快速生成高质量的文章。它不仅支持批量生成文章&#xff0c;还能通过智能分段、Markdown转HTML等功能优化内容。此外&…...

    java多线程学习笔记

    文章目录 关键词1.什么是多线程以及使用场景?2.并发与并行3.多线程实现3.1继承 Thread 类实现3.2Runnable 接口方式实现3.3Callable接口/Future接口实现3.4三种方式总结 4.常见的成员方法&#xff08;重点记忆&#xff09;94.1setName/currentThread/sleep要点4.2线程的优先级…...

    Manticore Search,新一代搜索引擎之王

    吊打ES&#xff0c;新一代搜索引擎之王 概述 Manticore Search 是一个开源的分布式搜索引擎&#xff0c;专注于高性能和低延迟的搜索场景。 它基于 Sphinx 搜索引擎开发&#xff0c;继承了 Sphinx 的高效索引和查询能力&#xff0c;并在分布式架构、实时搜索、易用性等方面进…...

    【MySQL】数据类型与表约束

    目录 数据类型分类 数值类型 tinyint类型 bit类型 小数类型 字符串类型 日期和时间类型 enum和set 表的约束 空属性 默认值 列描述 zerofill 主键 自增长 唯一键 外键 数据类型分类 数值类型 tinyint类型 MySQL中&#xff0c;整形可以是有符号和无符号的&…...

    CAG技术:提升LLM响应速度与质量

    标题&#xff1a;CAG技术&#xff1a;提升LLM响应速度与质量 文章信息摘要&#xff1a; CAG&#xff08;Cache-Augmented Generation&#xff09;通过预加载相关知识到LLM的扩展上下文中&#xff0c;显著减少了检索延迟和错误&#xff0c;从而提升了响应速度和质量。与传统的R…...

    上位机知识篇---Linux源码编译安装链接命令

    文章目录 前言第一部分&#xff1a;Linux源码编译安装1. 安装编译工具2. 下载源代码3. 解压源代码4. 配置5. 编译6. 测试&#xff08;可选&#xff09;7. 安装8. 清理&#xff08;可选&#xff09;9.注意事项 第二部分&#xff1a;链接命令硬链接&#xff08;Hard Link&#xf…...

    科研绘图系列:R语言绘制线性回归连线图(line chart)

    禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍加载R包数据下载导入数据数据预处理画图保存图片系统信息参考介绍 科研绘图系列:R语言绘制线性回归连线图(line chart) 加载R包 library(tidyverse) library(ggthemes) libra…...

    将ollama迁移到其他盘(eg:F盘)

    文章目录 1.迁移ollama的安装目录2.修改环境变量3.验证 背景&#xff1a;在windows操作系统中进行操作 相关阅读 &#xff1a;本地部署deepseek模型步骤 1.迁移ollama的安装目录 因为ollama默认安装在C盘&#xff0c;所以只能安装好之后再进行手动迁移位置。 # 1.迁移Ollama可…...

    电源设计和效率优化案例C01

    本文重点讲清楚三个非常重要的问题: 手把手教会计算电源的效率计算,包括线性电源和开关电源等 1-电源的上下管的 Qg和Rdson为什么是一对矛盾量? 2-单相30A的电流输出电源要求,对上下管子应该如何取舍这两个参数,为什么? 电源设计是硬件设计的核心组成部分,尤其事目前…...

    别再只用轮盘赌了!遗传算法选择算子实战对比:Python代码实现与性能调优心得

    遗传算法选择算子深度实战&#xff1a;从轮盘赌到锦标赛的Python优化指南 在解决复杂优化问题时&#xff0c;遗传算法展现出了惊人的适应能力。但许多开发者止步于基础的轮盘赌选择&#xff08;Roulette Wheel Selection&#xff09;&#xff0c;却不知不同选择策略对算法性能的…...

    农业大宗商品与气候数据融合:MCP架构下的数据工程实践

    1. 项目概述&#xff1a;当农业大宗商品遇上气候数据最近在做一个挺有意思的项目&#xff0c;核心是把农业大宗商品的数据和气候数据给打通了。听起来好像是个挺宏大的概念&#xff0c;对吧&#xff1f;其实说白了&#xff0c;就是想把“地里长的”和“天上变的”这两件事&…...

    从用户态到内核态:Linux Hook技术的全景实践与攻防解析

    1. Linux Hook技术入门&#xff1a;从概念到实践 第一次接触Hook技术是在十年前的一个安全分析项目中&#xff0c;当时需要监控某个可疑进程的行为。那时候我才明白&#xff0c;原来Linux系统里藏着这么多可以"截胡"程序执行的秘密通道。简单来说&#xff0c;Hook技术…...

    某恶意软件样本逆向分析报告

    1.概述样本来源&#xff1a;微步在线恶意软件名称&#xff1a;刘文博-关于北京体彩中心的问题反馈.exesha256&#xff1a;c28d23d8658abc1f5683c6b50239d5593eb7a274a3abec56124d7fb43fec1b642.行为分析该程序图标设为word文档图标&#xff0c;实际为exe文件&#xff0c;诱骗受…...

    CANdela Studio配置避坑指南:从10服务到Data Type,这些细节别踩雷

    CANdela Studio配置避坑指南&#xff1a;从10服务到Data Type&#xff0c;这些细节别踩雷 在汽车电子诊断功能开发中&#xff0c;CANdela Studio作为诊断数据库(CDD)的核心编辑工具&#xff0c;其配置精度直接影响着诊断协议栈的生成质量。许多工程师能够完成基础配置&#xff…...

    社区团购系统源码推荐:为什么越来越多团队开始关注 LikeShop 社区团购系统?

    如果你最近在研究&#xff1a;社区团购系统源码社区团购平台搭建团长分销系统私域社区团购社区自提系统你会发现一个现象&#xff1a;越来越多人开始提到&#xff1a;“LikeShop社区团购系统”。尤其是在&#xff1a;生鲜团购社区零售社群团购县域电商社区便利店私域卖货这些场…...

    如何为iOS 14.0-16.6.1设备安装TrollStore:TrollInstallerX完整指南

    如何为iOS 14.0-16.6.1设备安装TrollStore&#xff1a;TrollInstallerX完整指南 【免费下载链接】TrollInstallerX A TrollStore installer for iOS 14.0 - 16.6.1 项目地址: https://gitcode.com/gh_mirrors/tr/TrollInstallerX 如果你正在寻找一种可靠且简单的方法在i…...

    Sora 2视频生成质量跃升47%的关键——Gaussian Splatting空间梯度重参数化技术(独家逆向工程报告)

    更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;Sora 2视频生成质量跃升47%的实证观测与技术归因 近期在多个基准测试中&#xff0c;Sora 2在UCF101-VideoQA、TVD和VidBench v2上的平均PSNR提升达47.2%&#xff0c;SSIM同步上升39.8%&#xff0c;关键…...

    3个核心机制解密:如何让视频PPT提取工具智能识别每一页幻灯片

    3个核心机制解密&#xff1a;如何让视频PPT提取工具智能识别每一页幻灯片 【免费下载链接】extract-video-ppt extract the ppt in the video 项目地址: https://gitcode.com/gh_mirrors/ex/extract-video-ppt 你是否曾经面对长达数小时的会议录像&#xff0c;需要从中提…...