Content #
获取 goroutine id的方式有两种,分别是简单方式和 hacker 方式。
简单方式 #
通过 runtime.Stack 方法获取栈帧信息,栈帧信息里包含goroutine id。你可以看看上面 panic 时候的贴图,goroutine id 明明白白地显示在那里。 runtime.Stack 方法可以获取当前的 goroutine 信息,第二个参数为 true 会输出所有的 goroutine 信息,信息的格式如下:
goroutine 1 [running]:
main.main()
....../main.go:19 +0xb1
第一行格式为 goroutine xxx,其中 xxx 就是 goroutine id,你只要解析出这个 id 即可。解析的方法可以采用下面的代码:
func GoID() int {
var buf [64]byte
n := runtime.Stack(buf[:], false)
// 得到id字符串
idField := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0]
id, err := strconv.Atoi(idField)
if err != nil {
panic(fmt.Sprintf("cannot get goroutine id: %v", err))
}
return id
}
Hacker方式 #
了解了简单方式,接下来我们来看 hacker 的方式,这也是我们方案一采取的方式。
首先,我们获取运行时的 g 指针,反解出对应的 g 的结构。每个运行的 goroutine 结构的 g 指针保存在当前 goroutine 的一个叫做 TLS 对象中。
- 我们先获取到 TLS 对象;
- 再从 TLS 中获取 goroutine 结构的 g 指针;
- 再从 g 指针中取出 goroutine id。
需要注意的是,不同 Go 版本的 goroutine 的结构可能不同,所以需要根据 Go 的不同版本进行调整。当然了,如果想要搞清楚各个版本的 goroutine 结构差异,所涉及的内容又过于底层而且复杂,学习成本太高。怎么办呢?我们可以重点关注一些库。我们没有必要重复发明轮子,直接使用第三方的库来获取 goroutine id 就可以了。
好消息是现在已经有很多成熟的方法了,可以支持多个 Go 版本的 goroutine id,给你推荐一个常用的库:petermattis/goid。