go 并发模型 之 sync -- Mutex (四)

本文详细探讨了Go语言中sync.Mutex的实现原理,包括Barging、Handsoff和Spinning三种模式,以及Go 1.9引入的饥饿模式。分析了lock和unlock的内部逻辑,阐述了Mutex的基本结构和源码分析,揭示了如何解决锁的公平性问题。
摘要由CSDN通过智能技术生成

        


前言

前面的内存模型的章节里讲到  内存屏障

barrier 指令要求所有对内存的操作都必须要“扩散”到 memory 之后才能继续执行其他对 memory 的操作。

因此,我们可以用高级点的 atomic compare-and-swap,或者直接用更高级的锁,通常是标准库提供。


一、实现原理

实现模式

    Barging., Handsoff, Spinning

Barging: 这种模式是为了提高吞吐量,当锁被释放时,它会唤醒第一个等待者,然后把锁给第一个等待者或者给第一个请求锁的人。 提升吞吐量

        

 Handsoff:

当锁释放时候,锁会一直持有直到第一个等待者准备好获取锁。它降低了吞吐量,因为锁被持有,即使另一个 goroutine 准备获取它。

一个互斥锁的 handsoff 会完美地平衡两个goroutine 之间的锁分配,但是会降低性能,因为它会迫使第一个 goroutine 等待锁 

Spinning:自旋在等待队列为空或者应用程序重度使用锁时效果不错。Parking 和 Unparking goroutines 有不低的性能成本开销,相比自旋来说要慢得多。

        饥饿模式:Go 1.9 通过添加一个新的饥饿模式来解决下面例子中问题,该模式将会在释放时候触发 handsoff。所有等待锁超过一毫秒的 goroutine(也称为有界等待)将被诊断为饥饿。当被标记为饥饿状态时,unlock 方法会 handsoff 把锁直接扔给第一个等待者。 在饥饿模式下,自旋也被停用,因为传入的goroutines 将没有机会获取为下一个等待者保留的锁。

      实现原理

        先讲原理在讲源码。 只讲源码的都是耍流氓!

      lock

调用lock方法时

1.当锁处于初始的状态会直接调用CAS的方法获取锁 

Fast path: grab unlocked mutex.

  获取成功执行 race 检测 

 获取失败执行2

2.加锁失败会进入 lockSlow 函数        

   2.1 首先会判断是否可以进入自旋状态 如果可以进入自旋 最多自旋4次

  2.2 自旋结束计算当前锁的状态

  2.3 尝试根据CAS 获取锁 如果没有获取到就调用 runtime_SemacquireMutex 方法休眠当前 goroutine 

2.4 休眠结束 goroutine  会被唤醒  会先判断当前是否处在饥饿状态 

        2.4.1 如果当前 goroutine 超过 1ms 都没有获取到锁就会进饥饿模式

                设置唤醒和饥饿标记,重置迭代次数重新执行获取锁的循环

        2.4.1 如果处于饥饿模式就获得互斥锁 如果当前等待队列 只有当前的 goroutine

                就会退出等待队列

CAS 方法在这里指的是 atomic.CompareAndSwapInt32(addr, old, new) bool 方法,atomic包是由golang提供的low-level的原子操作封装,主要用来解决进程同步为题,官方并不建议直接使用。CompareAndSwapInt32()就是int32型数字的compare-and-swap实现。cas(&addr, old, new)的意

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值