sync.Mutex源码

sync.Mutex

go 复制代码
type Mutex struct {
	_ noCopy

	mu isync.Mutex
}

// isync "internal/sync"
type Mutex struct {
	state int32 // 一个 int32 里用位表示「是否加锁、是否唤醒中、是否饥饿、等待者个数」
	sema  uint32 // 信号量,用来在抢不到锁时 sleep / 被 Unlock 时 wake 一个 waiter。
}

const (
	mutexLocked      = 1 << 0   // 1:当前有人持有锁
	mutexWoken       = 1 << 1   // 2:已有 goroutine 被唤醒(或正在自旋),Unlock 不必再唤醒别人
	mutexStarving    = 1 << 2   // 4:饥饿模式
	mutexWaiterShift = 3        // 高 29 位:等待者数量
)

Lock

  1. 快速路径:当前 state == 0(没人持锁、没人等),CAS 成 mutexLocked 就拿到锁,直接 return。
  2. 否则lockSlow(),里面做自旋、排队、饥饿切换等。
go 复制代码
func (m *Mutex) Lock() {
	if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
		...
		return
	}
	m.lockSlow()
}
go 复制代码
func (m *Mutex) lockSlow() {
	...
	for {
		// 当前有人持锁且不是饥饿模式,开始自旋
		if old&(mutexLocked|mutexStarving) == mutexLocked && runtime_canSpin(iter) {
			if !awoke && old&mutexWoken == 0 && old>>mutexWaiterShift != 0 &&
				atomic.CompareAndSwapInt32(&m.state, old, old|mutexWoken) {
				//别去叫醒队列里的兄弟了,我已经在这等着抢了
				awoke = true
			}
			runtime_doSpin()
			iter++
			old = m.state
			continue
		}
		new := old
		if old&mutexStarving == 0 {
			new |= mutexLocked // 只要不是饥饿模式,就尝试抢锁
		}
		if old&(mutexLocked|mutexStarving) != 0 {
			new += 1 << mutexWaiterShift // 如果锁被占或在饥饿模式,自己乖乖去排队,等待者+1
		}
		if starving && old&mutexLocked != 0 {
			new |= mutexStarving // 如果我已经等了超过 1ms,就把锁标记为饥饿模式
		}
		...
		if atomic.CompareAndSwapInt32(&m.state, old, new) {
			if old&(mutexLocked|mutexStarving) == 0 {
				break // 成功抢到锁,大功告成
			}
			// 抢不到,调用信号量进入休眠
			queueLifo := waitStartTime != 0
			if waitStartTime == 0 {
				waitStartTime = runtime_nanotime()
			}
			runtime_SemacquireMutex(&m.sema, queueLifo, 2)
			// 被唤醒后,检查自己等了多久,如果太久,开启饥饿标记
			starving = starving || runtime_nanotime()-waitStartTime > starvationThresholdNs
			old = m.state
			// 饥饿模式的处理
			if old&mutexStarving != 0 {
				// 此时所有权直接从上一个 Unlock 的人手里传到了这个被唤醒的人手里
				...
				delta := int32(mutexLocked - 1<<mutexWaiterShift)
				if !starving || old>>mutexWaiterShift == 1 {
					// 如果我是最后一个人,或者我等的没那么久了,退出饥饿模式
					delta -= mutexStarving
				}
				atomic.AddInt32(&m.state, delta)
				break
			}
			awoke = true
			iter = 0
		} else {
			old = m.state
		}
	}

	if race.Enabled {
		race.Acquire(unsafe.Pointer(m))
	}
}

TryLock

  1. 若已经加锁 或处于饥饿(饥饿时锁要交给 waiter,不能给 TryLock),直接返回 false。
  2. 否则 CAS 把 mutexLocked 置 1,成功返回 true,失败返回 false。不排队、不自旋
go 复制代码
func (m *Mutex) TryLock() bool {
	old := m.state
	if old&(mutexLocked|mutexStarving) != 0 {
		return false
	}

	if !atomic.CompareAndSwapInt32(&m.state, old, old|mutexLocked) {
		return false
	}

	if race.Enabled {
		race.Acquire(unsafe.Pointer(m))
	}
	return true
}

UnLock

go 复制代码
func (m *Mutex) Unlock() {
	if race.Enabled {
		_ = m.state
		race.Release(unsafe.Pointer(m))
	}

	// Fast path: drop lock bit.
	new := atomic.AddInt32(&m.state, -mutexLocked)
	if new != 0 {
		// Outlined slow path to allow inlining the fast path.
		// To hide unlockSlow during tracing we skip one extra frame when tracing GoUnblock.
		m.unlockSlow(new)
	}
}
  • 检查 :若 (new+mutexLocked)&mutexLocked == 0,说明本来就没锁,fatal("unlock of unlocked mutex")
  • 非饥饿
    • 若没有 waiter,或已经有别人把锁/唤醒拿走了,直接 return。
    • 否则 CAS:waiter 数减 1、置上 mutexWoken ,成功则 runtime_Semrelease(&m.sema, ...) 唤醒一个 waiter。
  • 饥饿
    • 不抢锁,直接把锁交给队头 waiter
go 复制代码
func (m *Mutex) unlockSlow(new int32) {
	if (new+mutexLocked)&mutexLocked == 0 {
		fatal("sync: unlock of unlocked mutex")
	}
	if new&mutexStarving == 0 {
		old := new
		for {
			if old>>mutexWaiterShift == 0 || old&(mutexLocked|mutexWoken|mutexStarving) != 0 {
				return
			}
			// Grab the right to wake someone.
			new = (old - 1<<mutexWaiterShift) | mutexWoken
			if atomic.CompareAndSwapInt32(&m.state, old, new) {
				runtime_Semrelease(&m.sema, false, 2)
				return
			}
			old = m.state
		}
	} else {
		runtime_Semrelease(&m.sema, true, 2)
	}
}
相关推荐
人间凡尔赛11 分钟前
AI-Native 云原生架构:2026 年从容器编排到智能体编排的范式革命
后端·云原生·架构
IT_陈寒29 分钟前
Vue的响应式让我加班到凌晨3点,原来问题出在这
前端·人工智能·后端
卷无止境29 分钟前
提升 Python 代码健壮性的方法大盘点
后端·python
snow@li31 分钟前
Spring Boot:项目服务器完整部署教程(零基础可直接实操)
服务器·spring boot·后端
卷无止境1 小时前
Python 异常处理:从入门到工程实践
后端·python
AINative软件工程1 小时前
LLM 应用的熔断降级工程实践:Circuit Breaker 不只是重试的升级版
后端·llm·ai编程
程序员爱钓鱼1 小时前
Go 开发环境安装(Windows、macOS、Linux)
后端·面试·go
程序员爱钓鱼1 小时前
Rust String 与 &str 详解:字符串所有权、借用与转换
前端·后端·rust
阳光是sunny10 小时前
LangGraph中的Reducer是什么
前端·人工智能·后端