Go 互斥锁Mutex( 二 )

3、小结

Go 互斥锁Mutex

文章插图
4、自旋自旋是一种多线程同步机制,当前的进程在进入自旋的过程中会一直保持 CPU 的占用,持续检查某个条件是否为真 。
4.1、canSpinruntime_canSpin(iter)
  • CPU核数要大于1,否则自旋没有意义,因为此时不可能有其他协程释放锁
  • 当前Goroutine为了获取该锁进入自旋的次数 iter 小于四次
  • 当前机器上至少存在一个正在运行 Process
  • 处理的运行 G 队列为空,否则会延迟调度
它的实现方法链接到了sync_runtime_canSpin
4.2、doSpinruntime_doSpin()func sync_runtime_doSpin() { procyield(active_spin_cnt)}TEXT runtime·procyield(SB),NOSPLIT,$0-0 MOVL cycles+0(FP), AXagain: PAUSE SUBL $1, AX JNZ again RET它的实现方法链接到了 sync_runtime_doSpin
会执行 30 次 PAUSE指令,每执行一次再检查是否可以加锁,循环进行 。该过程中,进程仍是执行状态
4.3、优势更充分的利用CPU,尽量避免 goroutine 切换 。因为当前申请加锁的 goroutine 拥有CPU,如果经过短时间的自旋可以获得锁,当前协程可以继续运行,不必进入阻塞状态 。
对于新来进程一直进行自旋加锁,排队中的进程长时间无法拿到锁,则设置饥饿状态,该状态下不允许自旋 。
5、小结
Go 互斥锁Mutex

文章插图
  1. 上来先一个 CAS ,如果锁正空闲,并且没人抢,那么加锁成功;
  2. 否则,自旋几次,如果成功,也不用加入队列;
  3. 否则,加入队列;
  4. 从队列中被唤醒:
    1. 正常模式:和新来的一起抢锁,大概率失败
    2. 饥饿模式:肯定拿到锁

经验总结扩展阅读