go sync包(一) 互斥锁(一)

Sync包

sync包是go提供的用于并发控制的方法,类似于Java的JUC包。
在这里插入图片描述
(图片来自《go设计与实现》)

互斥锁 Mutex

Go 语言的 sync.Mutex 由两个字段 statesema 组成。

  • state 表示当前互斥锁的状态。
  • sema 是用于控制锁状态的信号量。
type Mutex struct {
    state int32
    sema  uint32
}

go的互斥锁功能相对比较简单:

mutex 本身没有包含持有这把锁的 goroutine 的信息,因此

  1. 无法实现可重入锁。
  2. 可能锁会被其他goroutine释放。

常见的错误场景

  • Lock / UnLock 不是成对出现
  • Copy 已使用的 Mutex(不可以复制)
    原因:Mutex 是一个有状态的字段,在并发环境下,状态时时在变化。
  • 重入
  • 死锁

状态

在这里插入图片描述
在默认情况下,互斥锁的所有状态位都是 0,int32 中的不同位分别表示了不同的状态:

  • mutexLocked — 表示互斥锁的锁定状态。
  • mutexWoken — 表示从正常模式被从唤醒。
  • mutexStarving — 当前的互斥锁进入饥饿状态。
  • waitersCount — 当前互斥锁上等待的 Goroutine 个数。
const (
   mutexLocked = 1 << iota // mutex is locked
   mutexWoken
   mutexStarving
   mutexWaiterShift = iota

   // Mutex fairness.
   //
   // Mutex can be in 2 modes of operations: normal and starvation.
   // In normal mode waiters are queued in FIFO order, but a woken up waiter
   // does not own the mutex and competes with new arriving goroutines over
   // the ownership. New arriving goroutines have an advantage -- they are
   // already running on CPU and there can be lots of them, so a woken up
   // waiter has good chances of losing. In such case it is queued at front
   // of the wait queue. If a waiter fails to acquire the mutex for more than 1ms,
   // it switches mutex to the starvation mode.
   //
   // In starvation mode ownership of the mutex is directly handed off from
   // the unlocking goroutine to the waiter at the front of the queue.
   // New arriving goroutines don't try to acquire the mutex even if it appears
   // to be unlocked, and don't try to spin. Instead they queue themselves at
   // the tail of the wait queue.
   //
   // If a waiter receives ownership of the mutex and sees that either
   // (1) it is the last waiter in the queue, or (2) it waited for less than 1 ms,
   // it switches mutex back to normal operation mode.
   //
   // Normal mode has considerably better performance as a goroutine can acquire
   // a mutex several times in a row even if there are blocked waiters.
   // Starvation mode is important to prevent pathological cases of tail latency.
   starvationThresholdNs = 1e6
)

sync.Mutex 有两种模式 — 正常模式和饥饿模式。

在正常模式下,锁的等待者会按照先进先出的顺序获取锁,但被唤醒的goroutine不拥有互斥锁,并与新到达的goroutine争夺所有权。
新来的goroutines有一个优势——它们已经在CPU上运行了,而且可能有很多,所以被唤醒的gorountine很有可能会失败。
为了减少这种情况的出现,一旦 Goroutine 超过 1ms 没有获取到锁,它就会将当前互斥锁切换饥饿模式,防止部分 Goroutine 饿死。
从性能上来看,如果我们能够把锁交给正在占用 CPU 时间片的 goroutine 的话,那就不需要做上下文的切换,在高并发的情况下,可能会有更好的性能。

饥饿模式是为了保证互斥锁的公平性。

在饥饿模式中,互斥锁会直接交给等待队列最前面的 Goroutine。新的 Goroutine 在该状态下不能获取锁、也不会进入自旋状态,它们只会在队列的末尾等待。如果一个 Goroutine 获得了互斥锁并且它在队列的末尾或者它等待的时间少于 1ms,那么当前的互斥锁就会切换回正常模式。

与饥饿模式相比,正常模式下的互斥锁能够提供更好地性能,饥饿模式的能避免 Goroutine 由于陷入等待无法获取锁而造成的高尾延时。

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值