C/C++中互斥量(锁)的实现原理探究

互斥量的实现原理探究

文章目录

  • 互斥量的实现原理探究
    • 互斥量的概念
    • 何为原子性操作
    • 原理探究

互斥量的概念

​ 互斥量(mutex)是一种同步原语,用于保护多个线程同时访问共享数据。互斥量提供独占的、非递归的所有权语义:一个线程从成功调用locktry_lock开始,到调用unlock结束,都拥有互斥量。

何为原子性操作

程序的原子性指:整个程序中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节。

原子性在一个操作是不可中断的,要么全部执行成功要么全部执行失败,有着 “同生共死” 的感觉。在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程所干扰

如果要保证原子性,必须符合以下两条规则:

  1. 运算结果并不依赖于变量的当前值,或者能够确保只有一个线程修改变量的值。

  2. 变量不需要与其他的状态变量共同参与不变约束。

原理探究

首先给出一段加锁场景的部分代码:

void route(ThreadData *td)
{
    // 加锁
    while (true)
    {
        pthread_mutex_lock(&td->_mutex);    // 加锁
        if (td->_tickets > 0)
        {
            // 模拟一次抢票的逻辑
            usleep(1000);
            printf("%s running, get tickets: %d\n", td->_name.c_str(), td->_tickets);
            td->_tickets--;
            pthread_mutex_unlock(&td->_mutex); // 解锁
            td->_total++;
        }
        else
        {
            pthread_mutex_unlock(&td->_mutex); // 解锁
            break;
        }
    }
}

上面这段代码模拟了抢票逻辑,将多线程并行抢票通过锁的加入变为串行执行,有效避免了恶意数据竞争(data race)

我们不妨假定有两个线程同时执行到 加锁指令 位置:

在这里插入图片描述

(上图左侧部分为加锁和解锁对应的汇编语言代码,其中每一行简单汇编指令的执行都是原子的)

不妨设定 thread-1 先进入 lock 逻辑 (thread-2先进入同理,不影响推断):

(这里的先进入 lock 逻辑,实际上指的是先执行左侧汇编语言中 xchgb &al, mutex 语句)

这就意味着 thread-1先执行交换语句,将系统指定初始的 mutex 值 (存储在内存中) 与寄存器初始值 0 进行交换,从而寄存器中值变为1。

在这里插入图片描述

由于汇编语言简单语句的单行执行是原子的,此时thread-1 已经执行完 xchgb &al, mutex 语句,所以不排除 thread-2 紧接着也执行 xchgb &al, mutex 语句的可能。(线程被切换的时机是随时的)

这时我们需要注意:

  • CPU寄存器的硬件只有一套,但是寄存器内的数据,属于线程的硬件上下文 !
  • 数据在内存中存储时,所有线程都能访问,属于共享资源,但是当数据从内存移动到寄存器时,就属于一个线程私有了 !

当执行线程从 thread-1 变为 thread-2 时,隶属于 thread-1 的寄存器硬件上下文被取走,thread-1::%al 寄存器值为1,CPU内%al寄存器值恢复为空。

所以,当 thread-2 执行 xchgb &al, mutex 语句时,访问到寄存器内存储的内容为自身线程所属寄存器的初始值(thread-2 先前执行了 moveb $0, %al ,所以初始值为0),由于内存中 mutex 初始值1已经被 thread-1 交换取走,此时内存中 mutex 的值为0,进行交换后 %al寄存器 中的值依然为0。

经过汇编的下层判断语句 if(%al寄存器内容 > 0) 不符合条件,故 thread-2 没有成功获得锁,需要执行 goto lock 语句重新申请锁的资源。

在这里插入图片描述

综上我们可以看到,所有线程在争锁的时候,只有一个 1 !!!

至此,我们发现 thread-2 想要继续执行,就必须等待 thread-1 释放锁,所以程序的执行流程就由 thread-1 执行到释放锁结束后,将内存中 mutex 变量置为1,thread-2 才终止等待,获得 thread-1 释放的锁后,执行自身的代码逻辑。

引入锁的用途就是为了解决并发访问出现的问题,其问题的本质是多个执行流同时执行访问全局数据的代码造成的。使用锁保护全局共享资源的本质是通过保护临界区完成的。

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode374猜数字大小

题目描述 我们正在玩猜数字游戏。猜数字游戏的规则如下:我会从 1 到 n 随机选择一个数字。 请你猜选出的是哪个数字。如果你猜错了,我会告诉你,我选出的数字比你猜测的数字大了还是小了。你可以通过调用一个预先定义好的接口 int guess(int n…

代码随想录算法训练营第四十四天 | 01背包问题 二维、 01背包问题 一维、416. 分割等和子集

01背包问题 二维 代码随想录 视频讲解:带你学透0-1背包问题!| 关于背包问题,你不清楚的地方,这里都讲了!| 动态规划经典问题 | 数据结构与算法_哔哩哔哩_bilibili 1.dp数组定义 dp[i][j] 下标为[0,i]之间的物品&…

2024盘古石初赛(服务器部分)

赛后总结 这次初赛就有20道服务器部分赛题,做的情况一般,错了5道题这样,主要原因就是出在第二个网站服务器没有重构起来 今天来复现一下 这次的服务器部分我直接用仿真仿起来就开找了 第一台IM前期配置 先把网配置好,然后ssh…

搭载算能 BM1684 芯片,面向AI推理计算加速卡

搭载算能 BM1684 芯片,是面向AI推理的算力卡。可集成于服务器、工控机中,高效适配市场上所有AI算法,实现视频结构化、人脸识别、行为分析、状态监测等应用,为智慧城市、智慧交通、智慧能源、智慧金融、智慧电信、智慧工业等领域进…

Django Celery技术详解

文章目录 简介安装和配置创建并调度任务启动Celery Worker在视图中调用异步任务拓展功能 简介 Django Celery 是一个为Django应用程序提供异步任务处理能力的强大工具。它通过与消息代理(如RabbitMQ、Redis)集成,可以轻松地处理需要长时间运…

QT5:调用qt键盘组件实现文本框输入

目录 一、环境与目标 二、Qt VirtualKeyboard 1.勾选Qt VirtualKeyboard 2.ui设计流程 3.注意事项及问题点 三、参考代码 参考博客 一、环境与目标 qt版本:5.12.7 windows 11 下的 Qt Designer (已搭建) 目标:创建一个窗…

系统架构设计师【第8章】: 系统质量属性与架构评估 (核心总结)

文章目录 8.1 软件系统质量属性8.1.1 质量属性概念8.1.2 面向架构评估的质量属性8.1.3 质量属性场景描述 8.2 系统架构评估8.2.1 系统架构评估中的重要概念8.2.2 系统架构评估方法 8.3 ATAM方法架构评估实践8.3.1 阶段1—演示(Presentation)8.3…

微信小程序教程DAY3

box标签 第二种方法 绿色第一种 第一种更好 效果一样 完成这个项目 先写循环

失之毫厘差之千里之load和loads

起源 最近在读pandas库的一些文档的时候,顺便也会将文档上的一些demo在编辑器中进行运行测试,其中在读到pandas处理Json数据这一节的时候,我还是像往常一样,将文档提供的demo写一遍,结果在运行的时候,直接…

AI边缘计算盒子在智慧交通的应用

方案背景 随着经济增长,交通出行需求大幅增长,但道路建设增长缓慢,交通供需矛盾日益显著,中心城区主要道路高峰时段交通拥堵严重,道路交通拥堵逐渐常态化,成为制约城市可持续发展的重要因素之一。 痛点问题…

前端Vue小兔鲜儿电商项目实战Day05

一、登录 - 整体认识和路由配置 1. 整体认识 登录页面的主要功能就是表单校验和登录退出业务 ①src/views/Login/index.vue <script setup></script><template><div><header class"login-header"><div class"container m-…

未来已来, AI将作为超级工具?

人工智能时代已来 1.AI将作为超级工具&#xff1a;AI是推动全产业数字化转型的高效工具&#xff0c;机遇比互联网时代大10倍&#xff0c;但只有1/3的机会留给初创企业。 2.硅谷AI市场分类中&#xff0c;特别看好开源平台&#xff0c;其将为初创企业和大企业提供更多选择。 3.…

封装了一个iOS对号成功动画

基本思路其实很简单&#xff0c;就是通过贝塞尔曲线画出路径&#xff0c;然后 使用CAShapeLayer 渲染路径&#xff0c;然后通过strokeEnd 动画实现 路径的效果&#xff0c;这里注意&#xff0c;这个过程中过遇到过一个问题&#xff0c;就是 对号动画完成之后&#xff0c;整个对…

Presto 从提交SQL到获取结果 源码详解(3)

物理执行计划 回到SqlQueryExecution.startExecution() &#xff0c;执行计划划分以后&#xff0c; // 初始化连接&#xff0c;获取Connect 元数据&#xff0c;添加会话&#xff0c;初始ConnectId metadata.beginQuery(getSession(), plan.getConnectors()); // 构建物理执行…

数据结构与算法笔记:基础篇 - 栈:如何实现浏览器的前进和后退功能?

概述 浏览器的前进、后退功能&#xff0c;你肯定很熟悉吧&#xff1f; 当依次访问完一串页面 a-b-c 之后&#xff0c;点击浏览器的后退按钮&#xff0c;就可以查看之前浏览过的页面 b 和 a。当后退到页面 a&#xff0c;点击前进按钮&#xff0c;就可以重新查看页面 b 和 c。但…

Linux编程基础 8.4:epoll工作模式

1 简介 poll机制的工作原理及流程与select类似&#xff0c;但poll可监控的进程数量不受select中第二个因素——fd_set集合容量的限制&#xff0c;用户可在程序中自行设置被监测的文件描述符集的容量&#xff0c;当然poll在阻塞模式下也采用轮询的方式监测文件描述符集&#xf…

【React】封装一个好用方便的消息框(Hooks Bootstrap 实践)

引言 以 Bootstrap 为例&#xff0c;使用模态框编写一个简单的消息框&#xff1a; import { useState } from "react"; import { Modal } from "react-bootstrap"; import Button from "react-bootstrap/Button"; import bootstrap/dist/css/b…

【LeetCode】38.外观数列

外观数列 题目描述&#xff1a; 「外观数列」是一个数位字符串序列&#xff0c;由递归公式定义&#xff1a; countAndSay(1) "1"countAndSay(n) 是 countAndSay(n-1) 的行程长度编码。 行程长度编码&#xff08;RLE&#xff09;是一种字符串压缩方法&#xff0c…

STL中list的模拟实现

目录 list模拟实现 list节点 list的push_back()函数 list的迭代器操作&#xff08;非const&#xff09; list的迭代器操作&#xff08;const&#xff09; list迭代器const 非const优化 list的insert()函数 list的erase()函数 list的pop_back() push_front() pop_front(…

数据结构:希尔排序

文章目录 前言一、排序的概念及其运用二、常见排序算法的实现 1.插入排序2.希尔排序总结 前言 排序在生活中有许多实际的运用。以下是一些例子&#xff1a; 购物清单&#xff1a;当我们去超市购物时&#xff0c;通常会列出一份购物清单。将购物清单按照需要购买的顺序排序&…