锁的底层原理

您所在的位置:网站首页 sync锁的底层原理 锁的底层原理

锁的底层原理

#锁的底层原理| 来源: 网络整理| 查看: 265

我们写各类系统应用,常见的网络变成,基本上都是并发编程,不论是多线程还是多进程、或者是协程、队列等。这些并发场景在对共享变量进行访问时,会带来数据不一致问题,这时就需要用到锁或者原子操作保证数据一致性。 一、常见并发场景 1、单一数值的增减,如:秒杀活动库存的递减,投票数的增加。 2、对象结构的增减,如:优惠券的发送,在线用户列表。 二、并发编程的几个概念 1、并发执行不一定是并行执行 2、单核cpu可以并发执行,不能并行执行 并行是同一时刻多任务的处理,并发是同一时间段内的多任务处理。 区别:多核的并行处理涉及到同一时刻多核同时读一行缓存,存在脏读和脏写;单核的并发处理涉及到处理任务的中间变量存在脏写。 三、锁的作用 1、避免并行运算中,共享数据的的读写安全问题; 2、并行执行时,在锁的位置只有一个程序可以获得锁,其他程序无法获得; 3、锁的出现使得并行执行得地方在锁的位置执行串行; 四、锁的底层实现类型 1、锁的形式 (1)、锁内存总线:针对内存读写操作,在总线上进行控制,限制程序的内存访问; (2)、锁缓存行:同一个缓存行的内容读写操作,cpu内部的高速缓存会保证一致性锁作用在同一个对象或者变量上。 现代cpu首先会在高速缓存上寻找,如果存在对象、变量就使用锁缓存行,否则使用锁内存总线;加锁对执行速度带来的不良影响,由上看加锁和解锁都是在高速缓存和总线上进行读写的,效率非常高,而出现的耗时问题主要在产生冲突时执行的串口化等待时间,以及上下文的切换。 2、CPU/缓存与锁 锁的底层实现原理与CPU/高速缓存有密切的联系,首先看一下CPU 在这里插入图片描述 内核独享L1、L2,共享L3;多颗CPU通过QPI进行连接,内存读写要通过内存总线,CPU与内存、磁盘、网络、外设等通信,都需要通过各种系统提供的系统总线。 编译器、CPU优化 编译器优化:重新代码排序,优先读操作(读更好的性能,因为cache中有共享数据,而写操作需要,会让数据失效) CPU优化:指令执行乱序,(多核心协同处理,自动优化和重排指令顺序) 编译器、CPU屏蔽 优化屏蔽:禁止编译器做屏蔽,按照代码逻辑顺序生成二进制代码, volatile关键词; 内存屏蔽:禁止CPU优化,防止指令重排序,保证数据的可见性,store barrier, load barrier, full barrier; 写屏障:阻塞直到把store buff中的数据刷到cache中; 读屏障:阻塞直到把Invalid Queue中的消息执行完毕; 全屏障:包括读写屏障,保证各核数据一致性。

那么golang中加锁加了什么? 1、禁止编译器做优化(优化屏蔽); 2、禁止CPU进行指令重排(内存屏蔽) 3、针对缓存行和内存总线的控制; 4、冲突时的任务等待队列。 常见锁总结: 自旋锁:只要没有锁上,就不断重试。 如果别的线程长期持有该锁,那么你这个线程就一直在 while while while 地检查是否能够加锁,浪费 CPU 做无用功。 优点:不切换上下文; 不足:烧CPU; 适用场景:冲突不多,等待时间不长的情况下,或者少次数的尝试自旋.

在这里插入图片描述

互斥锁: 操作系统负责线程调度,为了实现「锁的状态发生改变时再唤醒」就需要把锁也交给操作系统管理。 所以互斥器的加锁操作通常都需要涉及到上下文切换,操作花销也就会比自旋锁要大。 优点:简单高效; 不足:冲突等待时的上下文切换; 适用场景:绝大部分情况下都可以直接使用互斥锁 在这里插入图片描述 条件锁: 它解决的问题不是「互斥」,而是「等待」。 消息队列的消费者程序,在队列为空的时候休息,数据不为空的时候(条件改变)启动消费任务。 条件锁的业务针对性更强。 在这里插入图片描述 互斥锁操作: 在这里插入图片描述 在这里插入图片描述 大概的源码处理逻辑如下: 1 通过CAS操作来竞争锁的状态 &m.state; 2 没有竞争到锁,先主动自旋尝试获取锁 runtime_canSpin 和 runtime_doSpin (原地烧CPU); 3 自旋尝试失败,再次CAS尝试获取锁; 4 runtime_SemacquireMutex 锁请求失败,进入休眠状态,等待信号唤醒后重新开始循环; 5 m.state等待队列长度(复用的int32位数字,第一位是锁的状态,后31位是锁的等待队列长度计数器);



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3