這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
使用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:
- 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)
- 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。代碼如下所示:
12345678910111213141516171819202122232425262728293031323334 |
package mainimport ("fmt""runtime""strconv""strings""sync")func GoID() int {var buf [64]byten := 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.WaitGroupfor i := 0; i < 10; i++ {i := iwg.Add(1)go func() {defer wg.Done()fmt.Println(i, GoID())}()}wg.Wait()} |
go run main.go輸出:
1234567891011 |
main 19 140 51 62 75 106 113 87 124 98 13 |
它利用runtime.Stack的堆棧資訊。runtime.Stack(buf []byte, all bool) int會將當前的堆棧資訊寫入到一個slice中,堆棧的第一行為goroutine #### […,其中####就是當前的gororutine id,通過這個花招就實現GoID方法了。
但是需要注意的是,擷取堆棧資訊會影響效能,所以建議你在debug的時候才用它。
參考資料
- https://groups.google.com/forum/#!topic/golang-nuts/Nt0hVV_nqHE
- http://wendal.net/2013/0205.html
- http://blog.sgmansfield.com/2015/12/goroutine-ids/
- http://dave.cheney.net/2013/09/07/how-to-include-c-code-in-your-go-package
- http://golanghome.com/post/566
- https://github.com/t-yuki/goid
- https://github.com/petermattis/goid.git
- https://github.com/huandu/goroutine