Mutex

作者: wayyyy | 来源:发表于2022-04-05 00:56 被阅读0次
type Mutex struct {
    state int32
    sema uint32
}
  • Mutex.state
    表示互斥锁的状态,比如是否被锁定等,它是一个32位的整型变量,内部实现时把该变量分成4份:

    • Locked
      表示该Mutex 是否被锁定,0表示没有锁定,1表示已被锁定。
    • Woken
      表示是否有协程已被唤醒,0表示没有协程唤醒,1表示已有协程唤醒,正在加锁过程中。
    • Starving
      表示该Mutex 是否处于饥饿状态,0表示没有接,1表示饥饿状态,说明有协程阻塞了超过1ms。
    • Waiter
      表示阻塞等待锁的协程个数,协程解锁时根据此值来判断是否需要释放信号量。

    协程之间抢锁实际上是争抢给Locked赋值的权利,能给Locked域置1,就说明抢锁成功。抢不到就阻塞等待 信号量,一旦持有锁的协程解锁,那么等待的协程就会依次被唤醒。

    image.png
  • Mutex.sema
    表示信号量,协程阻塞等待该信号量,解锁的协程释放信号量从而唤醒等待信号量的协程

简单加锁
image.png
加锁被阻塞
image.png
简单解锁
image.png
解锁并唤醒协程
image.png
自旋

加锁时,如果当前的 Locked 位为1,说明当前该锁由其他协程持有,尝试加锁的协程并不马上转入阻塞,而是会持续地探测 Locked 位是否变为0,这个过程称为自旋。
自旋地时间很短,如果在自旋过程发现锁已经被释放,那么协程可以立即获取锁,此时即便有协程被唤醒,也无法获取锁,只能再次阻塞。自旋的好处是,当加锁失败时不必立即转入阻塞,有一定机会获取到锁,这样可以避免协程的切换。

  • 自旋条件
    加锁时程序会自动判断是否可以自旋,无限制的自旋将给CPU带来巨大的压力,所以判断是否自旋就很重要了。自旋必须满足下面的所有条件:
    • 自旋次数必须要足够少,通常为4,即自旋最多4次
    • CPU 核数要大于1,否则自旋没有意义,因此此时不可能有其他协程释放锁
    • 协程调度机制中的 Process 数量要大于1,比如使用 将处理器设置为1就不能启用自旋
    • 协程调度机制中的可运行队列必须为空,否则会延迟协程调度
mutex 模式
  • 自旋的问题
    如果自旋过程中获得的锁,那么之前被阻塞的协程将无法获得锁,如果加锁的协程特别多,每次都通过自旋获得锁,那么之前被阻塞的协程将很难获得锁,从而进入饥饿状态,为了避免协程长时间无法获取锁,自1.8版本之后增加了模式来解决。

每个Mutex 都有2种模式,称为 Normal 和 Starving

  • Normal
    Mutex 默认的模式,在该模式下,协程如果加锁不成功则不会立即转入阻塞排队,而是判断是否满足自旋的条件,如果满足则会启动自旋过程,尝试抢锁。

  • Starving
    自旋过程种抢到锁,一定意味着同一时刻同一时刻有协程释放了锁。释放锁时如果发现有阻塞等待的协程,那么还会释放一个信号量来唤醒一个等待的协程,被唤醒的协程得到了CPU后开始运行,此时发现锁已经被抢占了,自己只好再次阻塞,不过阻塞前会判断自上次阻塞到本次阻塞经过了多长时间,如果超过了1ms,则会将Mutex 标记为 Starving 模式,然后再次阻塞。

woken 状态

Woken 状态用于加锁和解锁过程中的通信,同一时刻,两个协程一个在加锁,另一个在解锁,在加锁的协程可能在自旋过程中,此时把woken 标记为1,用于通知解锁协程不必释放信号量。

相关文章

网友评论

      本文标题:Mutex

      本文链接:https://www.haomeiwen.com/subject/gjcixrtx.html