【Golang】性能调优 - 分析互斥锁使用

Posted by 西维蜀黍 on 2020-09-12, Last Modified on 2021-09-21

查看因持有互斥锁(mutexes)导致阻塞的情况(堆栈)

使用前需要先调用 runtime.SetBlockProfileRate

$ go tool pprof http://localhost:6060/debug/pprof/mutex
Fetching profile over HTTP from http://localhost:6060/debug/pprof/mutex
Saved profile in /Users/weishi/pprof/pprof.contentions.delay.002.pb.gz
Type: delay
Time: Aug 15, 2020 at 1:54pm (+08)
No samples were found with the default sample value type.
Try "sample_index" command to analyze different sample values.
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 0, 0% of 0 total
      flat  flat%   sum%        cum   cum%

Demo

package main

import (
	"net/http"
	_ "net/http/pprof"
	"runtime"
	"sync"
	"time"
)

func main() {
	runtime.GOMAXPROCS(1)
	runtime.SetMutexProfileFraction(1)
	runtime.SetBlockProfileRate(1)

	go func() {
		http.ListenAndServe(":6060", nil)
	}()

	for {
		stupidMutexUse()
	}

}

func stupidMutexUse() {
	m := &sync.Mutex{}
	m.Lock()
	go func() {
		time.Sleep(time.Second)
		m.Unlock()
	}()
	m.Lock()
}

可以看到,这个锁由主协程 Lock,并启动子协程去 Unlock,主协程会阻塞在第二次 Lock 这儿(等待1秒,直到子协程完成任务)。

因此,由于子协程足足睡眠了一秒,导致主协程等待这个锁的释放足足等了一秒钟。

在实际的业务代码中,我们使用 Mutex 很正常,但是如果监测到了类似这种主协程被长期阻塞的情况,就要考虑如果优化子协程中代码的实现了。

$ go tool pprof http://localhost:6060/debug/pprof/mutex
Fetching profile over HTTP from http://localhost:6060/debug/pprof/mutex
Saved profile in /Users/weishi/pprof/pprof.contentions.delay.011.pb.gz
Type: delay
Time: Aug 22, 2020 at 1:11pm (+08)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 8.02s, 100% of 8.02s total
      flat  flat%   sum%        cum   cum%
     8.02s   100%   100%      8.02s   100%  sync.(*Mutex).Unlock
         0     0%   100%      8.02s   100%  main.stupidMutexUse.func1
(pprof) list stupid
Total: 8.02s
ROUTINE ======================== main.stupidMutexUse.func1 in /Users/weishi/Working/GoPlayground/sw_pprof/sw_pprof.go
         0      8.02s (flat, cum)   100% of Total
         .          .     26:func stupidMutexUse() {
         .          .     27:	m := &sync.Mutex{}
         .          .     28:	m.Lock()
         .          .     29:	go func() {
         .          .     30:		time.Sleep(time.Second)
         .      8.02s     31:		m.Unlock()
         .          .     32:	}()
         .          .     33:	m.Lock()
         .          .     34:}

Reference