神刀安全网

如何得到goroutine 的 id?

使用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的方法。

1、C代码扩展

先前的一种方式是使用C语言暴露出获取goroutine方法,如

#include"runtime.h"  int64 ·Id(void) { returng->goid; } 
packageid  // Id returns the id of the current goroutine. // If you call this function you will go straight to hell. funcId()int64 

但是Go 1.4已经不支持在包中包含C代码 ( go1.4#gocmd )。

滴滴打车的huandu贡献了一个开源的项目 goroutine ,可以在新的Go版本下获取goroutine的id (至少go 1.5.1, go 1.6下可用):

首先获取这个项目的最新版本

goget-ugithub.com/huandu/goroutine 

然后你就可以在代码中调用相应的方法了:

// Get id of current goroutine. varidint64= goroutine.GoroutineId() println(id) 

2、第二种方法

还有一种花招,可以用纯Go语言获取当前的goroutine id。代码如下所示:

packagemain  import( "fmt" "runtime" "strconv" "strings" "sync" )  funcGoID()int{ varbuf[64]byte  n := runtime.Stack(buf[:], false)  idField := strings.Fields(strings.TrimPrefix(string(buf[:n]),"goroutine "))[0]  id, err := strconv.Atoi(idField) iferr !=nil{ panic(fmt.Sprintf("cannot get goroutine id: %v", err))  } returnid }  funcmain() {  fmt.Println("main", GoID()) varwg sync.WaitGroup fori :=0; i <10; i++ {  i := i  wg.Add(1) gofunc() { deferwg.Done()  fmt.Println(i, GoID())  }()  }  wg.Wait() } 

go run main.go 输出:

main1 914 05 16 27 510 611 38 712 49 813 

它利用 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

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » 如何得到goroutine 的 id?

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
分享按钮