自旋锁的实现原理

2023-05-16

自旋锁的实现原理

  1. 自旋锁的介绍
  • 自旋锁和互斥锁比较相似,都是为了实现保护共享资源而提出的一种锁机制,在任何一个时刻,只有一个执行单元可以获取该锁,如果该锁已经被别的单元占用,那么调用者便会进行CPU空转消耗并且时刻关注该所是否已经被释放,直到自己获取该锁为止。
  1. 自旋锁的特点
  • 相对于互斥锁,自旋锁是一种轻量级的锁,在有别的线程获取了该锁,需要进行自旋等待的时候,CPU依然占用着该线程资源不放,不会切换到其他线程去执行,因此,在等待时间较长的时候是不适用自旋锁的,这会拜拜消耗大量的CPU性能。
  • 而互斥锁,在需要进行等待的时候,是不会一直空转消耗CPU的,它会阻塞并且切换到别的线程执行,发生一个上下文切换,这也是一个较为耗时的操作,在重新再切换回该线程执行时,就已经发生了两次上下文切换。
  • 总的来说,在锁的竞争不繁忙,和该锁保持的代码执行时间较短的情况下,是可以使用自旋锁的,这样不会因为等待时间过长而白白浪费了大量的CPU性能。在C#中,SpinWait和SpinLock是基于自旋的等待操作,而Lock、Mutex和各种信号量,都是基于阻塞的等待操作。
  1. C#中的自旋操作
  • 如上所说,SpinWait和SpinLock是C#中的自旋操作,由于自旋锁的自旋操作带来了一定的风险性,比如活锁,CPU一直进行空转等待,所以在C#中的自旋操作是一种自适应的自旋操作,其部分源代码如下:
        internal const int YIELD_THRESHOLD = 10;//作上下文切换操作的阈值
        internal const int SLEEP_0_EVERY_HOW_MANY_TIMES = 5;//每自旋5次sleep(0)一次
        internal const int SLEEP_1_EVERY_HOW_MANY_TIMES = 20;//每自旋20次sleep(1)一次
        private int m_count;//自旋次数
        
        [__DynamicallyInvokable]
        public int Count
        {
            [__DynamicallyInvokable]
            get
            {
                return m_count;
            }
        }
        
        [_DynamicallyInvokable]
        public bool NextSpinWillYield
        {
            [__DynamicallyInvokable]
            get
            {
                if (m_count <= 10)//当自旋次数不超过10次时,单核CPU则返回true,
                {
                    return PlatformHelper.IsSingleProcessor;
                }
                return true;//自旋次数超过10次也将返回true
            }
        }
        
        [__DynamicallyInvokable]
        public void SpinOnce()
        {
            if (NextSpinWillYield)//如果下次操作将产生上下文切换
            {
                CdsSyncEtwBCLProvider.Log.SpinWait_NextSpinWillYield();
                //只有单核CPU才会m_count不大于10,多核CPU从10以后开始进行计数
                int num = (m_count >= 10) ? (m_count - 10) : m_count;
                if (num % 20 == 19)//10以后的计数值除以20后的余数为19则触发一次sleep(1)操作
                {
                    Thread.Sleep(1);
                }
                else if (num % 5 == 4)//10以后的计数值除以5后的余数为4则触发一次sleep(0)操作
                {
                    Thread.Sleep(0);
                }
                else//上述条件都不满足则进行Yield操作
                {
                    Thread.Yield();
                }
            }
            else
            {
                Thread.SpinWait(4 << m_count);//如果不发生上下文切换,这次自旋操作将导致线程等待4*2的m_count的时间
            }
            //当m_count到达int类型计数最大值时重新赋值为10,否则m_count加一
            m_count = ((m_count == 2147483647) ? 10 : (m_count + 1));
        }
        
        [__DynamicallyInvokable]
        public static bool SpinUntil(Func<bool> condition, int millisecondsTimeout)//该方法将导致线程在一定时间内自旋等待条件完成
        {
            if (millisecondsTimeout < -1)//超时时间不能为负
            {
                throw new ArgumentOutOfRangeException("millisecondsTimeout", millisecondsTimeout, Environment.GetResourceString("SpinWait_SpinUntil_TimeoutWrong"));
            }
            if (condition == null)//条件不能为空
            {
                throw new ArgumentNullException("condition", Environment.GetResourceString("SpinWait_SpinUntil_ArgumentNull"));
            }
            uint num = 0u;
            if (millisecondsTimeout != 0 && millisecondsTimeout != -1)
            {
                num = TimeoutHelper.GetTime();//获取当前时间
            }
            SpinWait spinWait = default(SpinWait);
            while (!condition())//条件不满足时,将执行自旋等待,超时时间等于0或者确实超时了将会返回false
            {
                if (millisecondsTimeout == 0)
                {
                    return false;
                }
                spinWait.SpinOnce();//执行自旋操作
                if (millisecondsTimeout != -1 && spinWait.NextSpinWillYield && millisecondsTimeout <= TimeoutHelper.GetTime() - num)
                {
                    return false;
                }
            }
            return true;//在指定超时时间内条件满足则返回false
        }
  • 在SpinWait中,在自旋次数超过10之后,每次进行自旋便会触发上下文切换的操作,在这之后每自旋5次会进行一次sleep(0)操作,每20次会进行一次sleep(1)操作。
  • 通过查看SpinOnce的源码,可以看到,在多核CPU的情况下,在自旋次数10次以内,每次自旋会导致该线程等待4*2的m_count的时间,自旋次数超过10次之后,每20次自旋的第19次会进行sleep(1)一次,每5次自旋的第4次会进行sleep(0)一次,其余都是yield()操作。
  • 对于该线程来说,yield()会导致CPU强制将当前线程切换为当前已经准备好的另外一个线程,即使这个线程的优先级更低,sleep(0)则是将当前线程重新放回该优先级的队列,重新进行一次线程调度,如果没有更高优先级或者相同优先级的就绪线程,CPU可能会再次调回该线程,而sleep(1)会使该线程在未来的1ms的时间内不会成为就绪状态,将不参与当前CPU的竞争。这三种方式都会直接强制该线程放弃剩余的当前的时间片,重新进行线程调度。
  • 再来看SpinUntil的源码,这个方法允许在一个指定的时间内,等待某条件的完成,首先它的指定时间不能为负,条件不能为null,接下来,便使用一个While循环,判断该条件是否满足,每判断一次条件,会判断时间是否已经到达,也会执行一次自旋操作SpinOnce(),若时间到达条件还没能满足则会返回false,在指定时间内满足了则返回true。这也是使用SpinOnce()的一种标准的操作,在C#的其他很多地方中对SpinOnce()方法的使用也是这样的。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

自旋锁的实现原理 的相关文章

  • GIT 基于TAG拉取分支

    git 基于tag拉branch 获得最新 span class token function git span origin fetch 从tag创建新的分支 span class token function git span bran
  • 栈的作用

    栈 栈 xff08 stack xff09 又名堆栈 xff0c 它是一种运算受限的线性表 其限制是仅允许在表的一端进行插入和删除运算 这一端被称为栈顶 xff0c 相对地 xff0c 把另一端称为栈底 向一个栈插入新元素又称作进栈 入栈或
  • C++中vector的用法详解

    vector 向量 C 43 43 中的一种数据结构 确切的说是一个类 它相当于一个动态的数组 当程序员无法知道自己需要的数组的规模多大时 用其来解决问题可以达到最大节约空间的目的 用法 1 文件包含 首先在程序开头处加上 include
  • ImportError: libQtGui.so.4: cannot open shared object file: No such file or directory

    报错 xff1a File home sx125 anaconda3 envs pytorch lib python3 7 site packages cv2 init py line 3 in from cv2 import Import
  • 批量更改YOLO标签类别

    原本的标签的classes txt文件中person类别是1 即第二行才是person类 xff0c 而后来找到的数据集大且标注好了 xff0c 好巧不巧person类别是0 故要将labels文件的类别都改成0 要自己先创建好空的文件夹存
  • 【CMake】编译和链接静态库和动态库

    项目结构工作原理 配置项目编译库 项目结构 span class token builtin class name span include myClass h src CMakeLists txt myClass cpp CMakeLis
  • 字符串比较大小

    1 规则 1 如果 字符串1的第n位的ASCII码值 等于 字符串2的第n位的ASCII码值 则 继续比较下一位 2 如果 字符串1的第n位的ASCII码值 大于 字符串2的第n位的ASCII码值 则 输出结果 1 表示字符串1 gt 字符
  • 将本地jar添加到Maven本地仓库

    在Maven项目中 xff0c 如果需要引入自己的jar包 xff0c 需要将jar添加到本地Maven仓库 方法一 xff1a 假设将包htmlparser jar放入了项目下的lib目录中 xff1a gt project lib ht
  • UART的奇偶校验

    1 奇校验 当数据位中 1 的个数为奇数时 xff0c 校验位为 0 xff0c 否则为 1 2 偶校验 当数据位中 1 的个数为偶数时 xff0c 校验位为 0 xff0c 否则为 1
  • windows 关闭占用端口的进程

    1 netstat ano findstr yourPortNumber 2 taskkill PID typeyourPIDhere F
  • Linux TCP server/client例程

    1 服务器端 span class token macro property span class token directive hash span span class token directive keyword include s
  • Nvidia Jetson 平台 DeepStream-6.0.1 部署 YoloV5-6.0 实现目标检测

    项目介绍 xff1a 在 Jetson 平台上利用 DeepStream 处理多路视频源 xff0c 并实现自己训练的 YoloV5 模型的部署 文章目录 前言1 YoloV5 模型训练自己的数据集1 1 建立自己的数据集1 1 1 开始之
  • 软路由保姆级入门教程 一篇看懂软路由

    前言 amp nbsp amp nbsp 玩张大妈也一年多了 xff0c 软路由改装 刷机文章写了不少 xff0c 很早就打算写篇软路由入门文章 xff0c 但是一直没落实 xff0c 原因有二 xff1a 圈子里大佬众多 xff0c 基础
  • CMake入门02-CMake中的静态库

    CMake中的静态库 静态库 文件树 CMakeLists txt include static Hello h src Hello cpp main cpp 1 1 Hello h 声明了Hello类 xff0c Hello的方法是pri
  • C++:struct与class的区别

    xff08 1 xff09 C语言中struct与class的区别 xff1a a struct只作为一种复杂数据类型定义的结构体 xff0c 不能用于面向对象编程 xff1b b C语言没有class关键字 xff08 2 xff09 C
  • Rplidar A1使用并改为ROS中3D点云输出(PointCloud2)

    这里写自定义目录标题 Rplidar A1使用并改为ROS中3D点云输出Rplidar安装测试修改后完整代码测试 Rplidar A1使用并改为ROS中3D点云输出 3D激光雷达价格不菲 xff0c 在研究过程中 xff0c 可以尝试将2D
  • Spring/SpringBoot常用注解总结!

    以下内容皆为转载 xff0c 转载地址 xff1a 接近8000字的Spring SpringBoot常用注解总结 xff01 安排 xff01 1 64 SpringBootApplication 这里先单独拎出 64 SpringBoo
  • MobaXterm 复制粘贴快捷键

    1 复制 不用设置 xff0c MobaXTerm 里面选取内容就已经复制了 xff0c 如图 xff0c 白色的内容就已经成功复制了哈哈哈哈 xff0c 真方便 注 xff1a 如果不行 xff0c 看看是否是这里没有勾上 xff08 在
  • Apollo配置中心

    1 Apollo 介绍 Apollo xff08 阿波罗 xff09 是携程框架部门研发的分布式配置中心 xff0c 能够集中化管理应用不同环境 不同集群的配置 xff0c 配置修改后能够实时推送到应用端 xff0c 并且具备规范的权限 流

随机推荐

  • Linux 命令

    1 Linux Shell 获取本地当前时间或前一分钟时间 1 1 获取前一分钟时间 xff1a 1 xff09 默认格式 date d 34 1 minute ago 34 date d 34 1 minute ago 34 Thu Oc
  • powershell 遍历数据库表导出为csv

    Write Output 0 server 61 34 34 database 61 34 dbname 34 tablequery 61 34 SELECT schemas name as schemaName tables name a
  • 回顾一:lili-om编译及运行问题(process has died、Leaf size is too small 等)[Livox Horizon激光雷达]

    度过考试周和作业周 xff0c 终于有时间可以搞自己的东西啦 xff0c 半个月前学习的东西忘得差不多了 xff0c 现在凭借着仅剩的记忆回顾一下 x1f604 末尾有小惊喜 xff0c 嘿嘿 xff01 1 Ceres Solver1 1
  • EXCEl 时间戳转换为日期格式

    1 EXCEl 时间戳转换为日期格式 公式为 xff1a 61 TEXT A2 1000 43 8 3600 86400 43 70 365 43 19 34 yyyy mm dd hh mm ss 34 具体操作如下 xff1a A2 1
  • 代码分析工具 - SonarQube

    1 常见代码质量分析工具 SonarQube xff1a 可以分析27多种不同编程语言中的代码 xff0c 并帮助您提高性能和检测安全漏洞 它由SonarSource的团队开发 xff0c 对社区免费开源 SonarQube可以添加到您的C
  • Lambda 表达式

    1 Lambda 表达式 1 1 通过接口传递代码 针对接口而非具体类型进行编程 xff0c 可以降低程序的耦合性 xff0c 提高灵活性 xff0c 提高复用性 接口常被用于传递代码 xff0c 比如 xff0c 我们知道 File 有如
  • Java 集合

    1 Java 集合框架 1 1 Java 集合概述 1 xff09 Java 容器 集合 数组都是对多个数据进行存储操作的结构 xff0c 简称 Java 容器 说明 xff1a 此时的存储 xff0c 主要指的是内存层面的存储 xff0c
  • 并发编程 - AQS 源码

    1 AQS 源码 public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java io Seriali
  • 解决C++中两个类的头文件互相包含问题

    转载 xff1a 134条消息 C 43 43 中两个类的头文件互相包含问题 道道道人间道的博客 CSDN博客 c 43 43 头文件互相包含 我们知道 xff0c 当一个类 xff08 设类A xff09 中包含另一个类 xff08 设类
  • 用递归的方法将整数n转换成字符串

    span class token keyword int span span class token function main span span class token punctuation span span class token
  • 关于&&和||的优先级问题

    span class token macro property span class token directive keyword include span span class token string lt stdio h gt sp
  • cpu与外设之间传送数据的时存在的问题

    一 速度不匹配 I O设备的速度比cpu慢很多 xff0c I O设备的不同速度差异也很大 二 时序不匹配 每个I O设备都有自己的定时控制电路 xff0c 无法与cpu取得统一 三 信息格式不匹配 不同的I O设备存储和处理信息的格式不同
  • 微机原理计算机基础部分简答题

    简述数据总线和地址总线各自具有的特点 xff0c 如果数据总线和地址总线采用同一组信号线有什么办法将地址总线分类出来 数据总线的特点为双向三态 xff0c 数据总线位数决定cpu一次传输二进制信息的位数 地址总线的特点为单向三态 xff0c
  • 部署IIS后dll not found

    问题 xff1a Could not load file or assembly 39 XX 39 or one of its dependencies An attempt was made to load a program with
  • 汇编指令对标志位的影响

    MOV LEA XCHG PUSH POP IN OUT 传送类指令不影响标志位 ADD ADC SUB SBB CMP 加减法指令影响全部标志位 INC DEC 增一减一指令不影响CF NEG 求补指令影响除CF外的5个标志位 xff0c
  • 微处理器部分简答题

    简述8086和8088cpu的相同点和不同点 相同点 xff1a 内部数据总线均为16位 xff0c 寄存器和指令系统完全兼容 不同点 xff1a 8086的外部数据总线为16位 xff0c 8088外部数据总线为8位 xff1b 8086
  • 大浪淘沙

    裸机指的是未装备任何软件的计算机 计算机的运行速度的单位是MI S xff0c 其含义是每秒钟执行百万条指令 影响cpu处理速度的主要因素是字长 主频 ALU 有无cache 冯诺依曼原理的基本思想是程序存储和程序控制 第一代计算机采用的电
  • 关于字符数组和字符指针

    span class token macro property span class token directive keyword include span span class token string lt stdio h gt sp
  • YOLOv5核心基础知识讲解

    我这主要是江大白老师的内容 xff01 xff01 深入浅出Yolo系列之Yolov3 amp Yolov4 amp Yolov5 amp Yolox核心基础知识完整讲解 xff08 CSDN xff09 深入浅出Yolo系列之Yolov5
  • 自旋锁的实现原理

    自旋锁的实现原理 自旋锁的介绍 自旋锁和互斥锁比较相似 xff0c 都是为了实现保护共享资源而提出的一种锁机制 xff0c 在任何一个时刻 xff0c 只有一个执行单元可以获取该锁 xff0c 如果该锁已经被别的单元占用 xff0c 那么调