mutex锁原理

1.mutex锁原理

01.Mutex

1.1 mutex结构体

  • 源码包src/sync/mutex.go:Mutex定义了互斥锁的数据结构
type Mutex struct {
	state int32     // 表示互斥锁的状态,比如是否被锁定等
	sema  uint32   // 表示信号量,协程阻塞等待该信号量,解锁的协程释放信号量从而唤醒等待信号量的协程
}
  • 我们看到Mutex.state是32位的整型变量,内部实现时把该变量分成四份,用于记录Mutex的四种状态。
  • 下图展示Mutex的内存布局
    • Locked: 表示该Mutex是否已被锁定,0:没有锁定 1:已被锁定。
    • Woken: 表示是否有协程已被唤醒,0:没有协程唤醒 1:已有协程唤醒,正在加锁过程中。
    • Starving:表示该Mutex是否处理饥饿状态, 0:没有饥饿 1:饥饿状态,说明有协程阻塞了超过1ms。
    • Waiter: 表示阻塞等待锁的协程个数,协程解锁时根据此值来判断是否需要释放信号量。

img

1.2 简单加锁

  • 假定当前只有一个协程在加锁,没有其他协程干扰,那么过程如下图所示
  • 加锁过程会去判断Locked标志位是否为0,如果是0则把Locked位置1,代表加锁成功
  • 从上图可见,加锁成功后,只是Locked位置1,其他状态位没发生变化

img

1.3 加锁被阻塞

  • 假定加锁时,锁已被其他协程占用了,此时加锁过程如下图所示
    • 当协程B对一个已被占用的锁再次加锁时,Waiter计数器增加了1
    • 此时协程B将被阻塞,直到Locked值变为0后才会被唤醒。

img

1.4 解锁并唤醒协程

  • 假定解锁时,有1个或多个协程阻塞,此时解锁过程如下图所示:
  • 协程A解锁过程分为两个步骤,一是把Locked位置0,二是查看到Waiter>0
  • 所以释放一个信号量,唤醒一个阻塞的协程,被唤醒的协程B把Locked位置1,于是协程B获得锁。

img

1.5 自旋过程

  • 加锁时,如果当前Locked位为1,说明该锁当前由其他协程持有,尝试加锁的协程并不是马上转入阻塞
  • 而是会持续的探测Locked位是否变为0,这个过程即为自旋过程。
  • 自旋时间很短,但如果在自旋过程中发现锁已被释放,那么协程可以立即获取锁。此时即便有协程被唤醒也无法获取锁,只能再次阻塞。
  • 自旋的好处是,当加锁失败时不必立即转入阻塞,有一定机会获取到锁,这样可以避免协程的切换。
  • 自旋条件
    • 自旋次数要足够小,通常为4,即自旋最多4次
    • CPU核数要大于1,否则自旋没有意义,因为此时不可能有其他协程释放锁
    • 协程调度机制中的Process数量要大于1,比如使用GOMAXPROCS()将处理器设置为1就不能启用自旋
    • 协程调度机制中的可运行队列必须为空,否则会延迟协程调度

02.原子操作与锁

2.1 什么是原子操作

  • 一个或者多个操作在 CPU 执行的过程中不被中断的特性,称为原子性(atomicity)
  • 这些操作对外表现成一个不可分割的整体,他们要么都执行,要么都不执行,外界不会看到他们只执行到一半的状态。
  • atomic包中的原子操作则由底层硬件指令直接提供支持,这些指令在执行的过程中是不允许中断的
  • 因此原子操作可以在lock-free的情况下保证并发安全,并且它的性能也能做到随CPU个数的增多而线性扩展。

2.2 互斥锁跟原子操作

  • 使用目的:互斥锁是用来保护一段逻辑,原子操作用于对一个变量的更新保护。
  • 底层实现:
    • Mutex操作系统的调度器实现,而atomic包中的原子操作则由**底层硬件指令**直接提供支持,这些指令在执行的过程中是不允许中断的
    • 因此原子操作可以在lock-free的情况下保证并发安全,并且它的性能也能做到随CPU个数的增多而线性扩展。

2.3 Mutex实现机制

  • CAS (Compare And Swap) 的做法类似操作数据库时常见的乐观锁机制

  • 该操作在进行交换前首先确保被操作数的值未被更改,满足此前提条件下才进行交换操作。

  • 其实Mutex的底层实现也是依赖原子操作中的CAS实现的,原子操作的atomic包相当于是sync包里的那些同步原语的实现依赖。

  • 比如互斥锁Mutex的结构里有一个state字段,其是表示锁状态的状态位。

    type Mutex struct {
        state int32
        sema  uint32
    }

mutex锁原理
http://coderedeng.github.io/2021/02/23/Go进阶 - mutex锁原理/
作者
Evan Deng
发布于
2021年2月23日
许可协议