GoLand 2025.3 Help

Mutex 分析器

Go 中的互斥锁分析器基于 pprof 包开发。 它会跟踪 sync.Mutexsync.RWMutex 的争用事件,展示 goroutine 在等待获取锁所花的时间。 在 GoLand 中,这些数据是通过 Go 的内置 runtime/pprof 机制收集的,并通过火焰图、调用树和方法列表进行可视化展示。

Mutex 分析

Mutex 分析器有助于识别由多个 goroutine 争用共享资源而引起的锁争用。它显示 goroutine 在等待获取 mutex 锁时被阻塞的频率和持续时间。 它显示 goroutine 在等待获取 mutex 锁时被阻塞的频率和持续时间。

示例程序

以下示例模拟多个 goroutine 递增由 mutex 保护的共享计数器。 由于所有 goroutine 都尝试锁定同一个 mutex,因此争用会迅速加剧:

package main import ( "sync" ) func Increment(n int) int { var mu sync.Mutex count := 0 wg := sync.WaitGroup{} wg.Add(n) for i := 0; i < n; i++ { go func() { mu.Lock() count++ mu.Unlock() wg.Done() }() } wg.Wait() return count } func main() { println("Final count:", Increment(100000)) }

尽管该函数可以正确计数到 n ,但在多个 goroutine 同时运行时,共享 mutex 上的严重争用会显著降低性能。

创建分析测试

若要衡量 goroutine 等待 mutex 的时间,请创建一个单元测试:

package main import "testing" func TestIncrement(t *testing.T) { total := Increment(100000) t.Log("Final count:", total) }

运行 Mutex 分析器

  1. 请打开 _test.go 文件。

  2. 点击装订区域中测试函数旁的 运行 图标。

  3. 请选择 使用 Mutex Profiler 进行分析

    使用 Mutex 分析器进行分析

分析 Mutex 分析结果

GoLand 提供三种视图显示 Mutex 分析数据:

  • 火焰图 :显示函数调用及 goroutine 等待(未运行)所花的时间。 每个块表示调用堆栈中的一个函数。 Y 轴表示堆栈深度(从下至上),X 轴则显示按升序排列的堆栈分析结果 —— 可按延迟次数(选中 争用 )或总等待时间(选中 延迟 )排序。

    火焰图 选项卡中,将鼠标悬停在任意块上可查看详细信息。

  • 调用树 :结合延迟统计数据可视化函数调用,可按延迟次数(选中 争用 )或总等待时间(选中 延迟 )排序。 数据按降序排列,以突出贡献最多阻塞时间的函数。 要配置或筛选 调用树 视图,请使用 表示设置 按钮 Presentation Settings 按钮

  • 方法列表 :按争用次数对所有方法进行排序。 反向跟踪 选项卡显示所选方法被调用的位置,而 合并的被调用方 选项卡则显示以该方法为起点的调用轨迹。 被调用方列表 是汇总调用层次结构中所有方法的方法列表。

优化并比较结果

为减少争用,您可以使用原子操作或将负载分散到多个锁上。 如下版本使用原子计数器代替单个 mutex:

package main import ( "sync/atomic" ) func IncrementAtomic(n int) int64 { var count int64 wg := sync.WaitGroup{} wg.Add(n) for i := 0; i < n; i++ { go func() { atomic.AddInt64(&count, 1) wg.Done() }() } wg.Wait() return count }

重新运行 Mutex 分析器后,火焰图几乎不再显示阻塞活动,表示使用原子操作消除了锁争用并提高了吞吐量。

重新运行 Mutex 分析器

基准测试与比较

添加基准测试以衡量 mutex 版本与原子版本之间的性能差异。

import "testing" func BenchmarkIncrement_Mutex(b *testing.B) { for i := 0; i < b.N; i++ { _ = Increment(100000) } } func BenchmarkIncrement_Atomic(b *testing.B) { for i := 0; i < b.N; i++ { _ = IncrementAtomic(100000) } }

运行基准测试

  1. 打开基准测试 _test.go 文件。

  2. 右键点击测试文件并导航至 运行 | gobench。 或者,您也可以使用终端: go test -bench . -benchmem

    run-mutex-benchmarks

预期结果:原子版本通常完成得更快,并且分配次数更少,在高争用情况下可展现更高的吞吐量。 具体数值因机器与 n 而异。

最后修改日期: 2025年 12月 5日