Go语言在Mutex包中提供了互斥锁,sync.Mutex
**

  1. type Mutex struct {
  2. state int32
  3. sema int32
  4. }

互斥锁的状态

互斥锁通常保持两种状态 正常模式饥饿模式
引入饥饿模式的原因是,为了保持互斥锁的公平性。
正常模式下,锁资源一般会交给刚被唤醒的goroutine,而为了怕部分goroutine被“饿死”,所以引入了饥饿模式,在饥饿模式下,goroutine在释放锁资源的时候会将锁资源交给等待队列中的下一个goroutine。

加锁逻辑

  1. func (m *Mutex) Lock() {
  2. // Fast path: grab unlocked mutex.
  3. if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
  4. if race.Enabled {
  5. race.Acquire(unsafe.Pointer(m))
  6. }
  7. return
  8. }
  9. // Slow path (outlined so that the fast path can be inlined)
  10. m.lockSlow()
  11. }

首先使用CAS方法判断是否可以直接获取锁资源

CAS指的是CompareAndSwap,也就是如果可以获得锁资源,则修改Mutex.state中的locked位,并成功获取,如果获取不到,则执行lowSlow()方法

比较并交换(compare and swap, CAS),是原子操作的一种,可用于在多线程编程中实现不被打断的数据交换操作,从而避免多线程同时改写某一数据时由于执行顺序不确定性以及中断的不可预知性产生的数据不一致问题。 该操作通过将内存中的值与指定数据进行比较,当数值一样时将内存中的数据替换为新的值。

method lockSlow


  1. func (m *Mutex) lockSlow() {
  2. var waitStartTime int64
  3. starving := false
  4. awoke := false
  5. iter := 0
  6. old := m.state
  7. for {
  8. // 省略
  9. }
  10. if race.Enabled {
  11. race.Acquire(unsafe.Pointer(m))
  12. }
  13. }

首先lowSlow会尝试使用CAS获取锁资源,如果获取不到,初始化当前goroutine需要的变量,执行for循环尝试获取锁资源