Go sync.Mutex:正常模式和饥饿模式

这是关于在 Go 中处理并发的系列文章的一部分:

Go sync.Mutex

  • 用于确保同一时间只有一个 goroutine 能操作共享资源,如代码、整数、映射、结构体、通道等。
  • LockUnlockTryLock三个主要操作,Lock后其他 goroutine 需等待解锁。
  • 以简单计数器为例,未加锁时结果不是 1000,是因为存在“竞态条件”,counter++不是原子操作。
  • 使用atomic包可处理该问题,本文重点介绍sync.Mutex的解决方案,使用mutex.Lockmutex.Unlock包裹临界区,结果为 1000。
  • 设置GOMAXPROCS为 1 时结果也为 1000,因为 goroutine 不会并行运行。

Mutex 结构:解剖

  • sync.Mutex结构包含state(32 位整数)和semauint32)两个字段。
  • state字段编码 mutex 的各种状态信息,如锁定、唤醒、饥饿等,sema作为信号量管理等待的 goroutine。

Mutex 锁流程

  • mutex.Lock有快速路径(处理未被占用的 mutex)和慢速路径(处理异常情况)。
  • 快速路径利用 CAS 操作,若失败则进入慢速路径,慢速路径中 goroutine 会自旋尝试获取锁,若失败会增加等待者计数并进入等待队列。
  • mutex 有普通模式和饥饿模式,普通模式下等待的 goroutine 按 FIFO 排队竞争,饥饿模式下会直接将锁传递给等待队列头部的 goroutine。

Mutex 解锁流程

  • 解锁流程也有快速路径和慢速路径,快速路径直接清除锁定位,若状态不为零则进入慢速路径。
  • 慢速路径根据 mutex 的模式进行不同处理,普通模式下尝试唤醒等待的 goroutine,饥饿模式下直接将锁传递给等待队列头部的 goroutine。

最后介绍了相关文章和 VictoriaMetrics 相关信息,VictoriaMetrics 是一个快速、开源且节省成本的监控服务工具,作者是 Gophers 爱好者。

阅读 21
0 条评论