如何得到goroutine 的 id?

smallnest · · 1394 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

使用Java的时候很容易得到线程的名字, 比如"Thread.currentThread().getName",这样就可以进行一些监控操作或者设置线程相关的一些数据。当转向Golang开发的时候,却发现Go语言并没有提供获取当前goroutine id的操作。这是Golang的开发者故意为之,避免开发者滥用goroutine id实现goroutine local storage (类似java的"thread-local" storage), 因为goroutine local storage很难进行垃圾回收。因此尽管以前暴露出了相应的方法,现在已经把它隐藏了。

Please don't use goroutine local storage. It's highly discouraged. In fact, IIRC, we used to expose Goid, but it is hidden since we don't want people to do this.

Potential problems include:

  1. when goroutine goes away, its goroutine local storage won't be GCed. (you can get goid for the current goroutine, but you can't get a list of all running goroutines)
  2. what if handler spawns goroutine itself? the new goroutine suddenly loses access to your goroutine local storage. You can guarantee that your own code won't spawn other goroutines,
    but in general you can't make sure the standard library or any 3rd party code won't do that.

thread local storage is invented to help reuse bad/legacy code that assumes global state, Go doesn't have legacy code like that, and you really should design your code so that state is passed explicitly and not as global (e.g. resort to goroutine local storage)

当然Go的这种隐藏的做法还是有争议的,有点因噎废食。在debug log的时候goroutine id是很好的一个监控信息。本文介绍了两种获取goroutine id的方法。

可以用纯Go语言获取当前的goroutine id。代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package main
import (
"fmt"
"runtime"
"strconv"
"strings"
"sync"
)
func GoID() int {
var buf [64]byte
n := runtime.Stack(buf[:], false)
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
}
func main() {
fmt.Println("main", GoID())
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
i := i
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println(i, GoID())
}()
}
wg.Wait()
}

go run main.go输出:

1
2
3
4
5
6
7
8
9
10
11
main 1
9 14
0 5
1 6
2 7
5 10
6 11
3 8
7 12
4 9
8 13

它利用runtime.Stack的堆栈信息。runtime.Stack(buf []byte, all bool) int会将当前的堆栈信息写入到一个slice中,堆栈的第一行为goroutine #### […,其中####就是当前的gororutine id,通过这个花招就实现GoID方法了。

但是需要注意的是,获取堆栈信息会影响性能,所以建议你在debug的时候才用它。

参考资料

  1. https://groups.google.com/forum/#!topic/golang-nuts/Nt0hVV_nqHE
  2. http://wendal.net/2013/0205.html
  3. http://blog.sgmansfield.com/2015/12/goroutine-ids/
  4. http://dave.cheney.net/2013/09/07/how-to-include-c-code-in-your-go-package
  5. http://golanghome.com/post/566
  6. https://github.com/t-yuki/goid
  7. https://github.com/petermattis/goid.git
  8. https://github.com/huandu/goroutine

有疑问加站长微信联系(非本文作者)

本文来自:鸟窝

感谢作者:smallnest

查看原文:如何得到goroutine 的 id?

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1394 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传