Go語(yǔ)言如何獲取goroutine的id
如果你使用過如 Python、Java 等主流支持并發(fā)的編程語(yǔ)言,那么通常都能夠比較容易的獲得進(jìn)程和線程的 id。但是在 Go 語(yǔ)言,沒有直接提供對(duì)多進(jìn)程和多線程的支持,而是提供了 goroutine 來(lái)支持并發(fā)編程。不過在 Go 中,獲取 goroutine 的 id 并不像其他編程語(yǔ)言那樣容易,但依然有辦法,本文就來(lái)介紹下如何實(shí)現(xiàn)。
獲取當(dāng)前進(jìn)程的 id
首先,雖然 Go 沒有提供多進(jìn)程編程,但啟動(dòng) Go 程序還是會(huì)有一個(gè)進(jìn)程存在的,Go 標(biāo)準(zhǔn)庫(kù)提供了 os.Getpid
函數(shù),可以方便的獲取當(dāng)前進(jìn)程的 id:
github.com/jianghushinian/blog-go-example/blob/main/goroutine/id/pid/main.go
package main import ( "fmt" "os" ) func main() { // 獲取當(dāng)前進(jìn)程的 id pid := os.Getpid() fmt.Println("process id:", pid) }
調(diào)用 os.Getpid()
會(huì)返回當(dāng)前進(jìn)程的 pid(進(jìn)程 id)。
獲取當(dāng)前 goroutine 的 id
Go 并沒有直接提供獲取 goroutine id 的方法,因?yàn)?goroutine 的管理是由 Go 運(yùn)行時(shí)(Go runtime)負(fù)責(zé)的,它并不暴露每個(gè) goroutine 的 id。然而,有一些方法可以間接獲取到與 goroutine 相關(guān)的信息。
使用 runtime 包獲取 goroutine id
雖然不能直接獲取每個(gè) goroutine 的 id,但我們可以變相的通過 runtime.Stack
函數(shù)來(lái)獲取。
實(shí)現(xiàn)代碼如下:
github.com/jianghushinian/blog-go-example/blob/main/goroutine/id/main.go
package main import ( "fmt" "runtime" "strconv" "strings" "sync" ) func GoId() int { buf := make([]byte, 32) 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() }
這段代碼通過自定義的 GoId
函數(shù)來(lái)獲取當(dāng)前 goroutine 的 id,并在 main
函數(shù)的主 goroutine 和子 goroutines 中打印它們的 id。
我們來(lái)解釋下 GoId
函數(shù)主要邏輯的實(shí)現(xiàn):
1.runtime.Stack(buf, false):
runtime.Stack
是runtime
包提供的公開函數(shù),用于獲取當(dāng)前 goroutine 的堆棧信息。- 參數(shù)
buf
是一個(gè)字節(jié)數(shù)組,用來(lái)存儲(chǔ)調(diào)用runtime.Stack()
返回的堆棧信息。 - 第二個(gè)參數(shù)是
false
,表示我們只獲取當(dāng)前 goroutine 的棧信息,如果為true
則是獲取所有 goroutines 的棧信息。 runtime.Stack()
會(huì)將當(dāng)前 goroutine 的棧信息寫入buf
中,n
是返回的字節(jié)數(shù),表示堆棧信息的長(zhǎng)度。
2.strings.TrimPrefix(string(buf[:n]), "goroutine "):
goroutine 1 [running]:
- 將堆棧信息轉(zhuǎn)為字符串并去掉前綴
"goroutine "
。 - 堆棧信息的第一行格式通常如下:
- 這里通過
TrimPrefix
去除前綴"goroutine "
后,剩下的內(nèi)容就是 goroutine 的 id 及其狀態(tài)信息。
3.idField := strings.Fields(...)[0]:
strings.Fields
將經(jīng)過TrimPrefix
處理后的字符串按空格切割成一個(gè)字符串切片。- 從切片中獲取第一個(gè)字段,這就是 goroutine 的 id,如
1
。
如果一切順利,GoId
函數(shù)最終返回當(dāng)前 goroutine 的 id。
main
函數(shù)實(shí)現(xiàn)則比較簡(jiǎn)單,先調(diào)用 GoId()
打印主 goroutine 的 id,然后啟動(dòng)了 10 個(gè)子 goroutine 并分別打印它們的 id。
執(zhí)行示例代碼,得到如下輸出:
$ go run main.go
main 1
9 29
0 20
5 25
6 26
7 27
8 28
2 22
1 21
4 24
3 23
這樣,我們就變相的通過先獲取堆棧信息,然后再?gòu)亩褩P畔⒅羞M(jìn)行解析的方式拿到了 goroutine 的 id。想必你也能夠發(fā)現(xiàn),這種實(shí)現(xiàn)方式性能不高,所以不到萬(wàn)不得已,不要輕易獲取 goroutine 的 id。
那么有沒有更高效的方式呢?
很遺憾,Go 官方?jīng)]有提供。不過有第三方庫(kù)幫我們實(shí)現(xiàn)了。
使用第三方庫(kù)獲取 goroutine id
一個(gè)比較常用的庫(kù)是 github.com/petermattis/goid,可以用來(lái)獲取當(dāng)前 goroutine 的 id。
安裝方式:
$ go get github.com/petermattis/goid
使用示例:
github.com/jianghushinian/blog-go-example/blob/main/goroutine/id/goid/main.go
package main import ( "fmt" "sync" "github.com/petermattis/goid" ) func main() { fmt.Println("main", goid.Get()) var wg sync.WaitGroup for i := 0; i < 10; i++ { i := i wg.Add(1) go func() { defer wg.Done() fmt.Println(i, goid.Get()) }() } wg.Wait() }
執(zhí)行示例代碼,得到如下輸出:
$ go run goid/main.go
main 1
9 43
4 38
5 39
6 40
7 41
8 42
1 35
0 34
2 36
3 37
我們僅需要調(diào)用 goid.Get()
即可獲取當(dāng)前 goroutine 的 id。
goid
庫(kù)使用了 C 和 匯編來(lái)獲取 goroutine id,所以性能更好。并且 goid
為所有的 Go 版本都做了兼容,從項(xiàng)目文件名可以看出,不同 Go 版本有著不同的實(shí)現(xiàn):
$ tree goid goid ├── LICENSE ├── README.md ├── go.mod ├── goid.go ├── goid_gccgo.go ├── goid_go1.3.c ├── goid_go1.3.go ├── goid_go1.4.go ├── goid_go1.4.s ├── goid_go1.5.go ├── goid_go1.5.s ├── goid_slow.go ├── goid_test.go ├── runtime_gccgo_go1.8.go ├── runtime_go1.23.go ├── runtime_go1.5.go ├── runtime_go1.6.go └── runtime_go1.9.go 1 directory, 18 files
在 goid_go1.3.c
中可以看到 C 語(yǔ)言版本實(shí)現(xiàn)如下:
github.com/petermattis/goid/blob/master/goid_go1.3.c
// +build !go1.4 #include <runtime.h> void ·Get(int64 ret) { ret = g->goid; USED(&ret); }
在 goid_go1.4.s
中可以看到匯編語(yǔ)言版本實(shí)現(xiàn)如下:
github.com/petermattis/goid/blob/master/goid_go1.4.s
// +build amd64 amd64p32 arm 386 // +build go1.4,!go1.5 #include "textflag.h" #ifdef GOARCH_arm #define JMP B #endif TEXT ·getg(SB),NOSPLIT,$0-0 JMP runtime·getg(SB)
此外,為了保證兼容性,在 goid.go
中還有一個(gè) Go 語(yǔ)言版本實(shí)現(xiàn):
github.com/petermattis/goid/blob/master/goid.go
package goid import ( "bytes" "runtime" "strconv" ) func ExtractGID(s []byte) int64 { s = s[len("goroutine "):] s = s[:bytes.IndexByte(s, ' ')] gid, _ := strconv.ParseInt(string(s), 10, 64) return gid } // Parse the goid from runtime.Stack() output. Slow, but it works. func getSlow() int64 { var buf [64]byte return ExtractGID(buf[:runtime.Stack(buf[:], false)]) }
這里 Go 版本的實(shí)現(xiàn)同樣使用 runtime.Stack()
,并且注釋也標(biāo)明了這個(gè)實(shí)現(xiàn)比較慢。
所以,如果我們真的需要獲取 goroutine 的 id,那么推薦使用 goid。
總結(jié)
在 Go 中獲取當(dāng)前進(jìn)程的 id 可以使用 os.Getpid()
函數(shù)。如果要獲取當(dāng)前 goroutine 的 id 則要困難一些,Go 標(biāo)準(zhǔn)庫(kù)沒有直接提供該功能,不過我們可以變相的從 runtime.Stack()
返回的堆棧信息中獲取,也可以使用第三方庫(kù) goid 來(lái)獲取。
以上就是Go語(yǔ)言如何獲取goroutine的id的詳細(xì)內(nèi)容,更多關(guān)于Go獲取goroutine的id的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
使用golang在windows上設(shè)置全局快捷鍵的操作
最近在工作中,總是重復(fù)的做事,想著自己設(shè)置一個(gè)快捷鍵實(shí)現(xiàn)windows 剪貼板的功能,所以本文小編給大家分享了使用golang在windows上設(shè)置全局快捷鍵的操作,文中有相關(guān)的代碼示例供大家參考,需要的朋友可以參考下2024-02-02Go?gRPC進(jìn)階教程服務(wù)超時(shí)設(shè)置
這篇文章主要為大家介紹了Go?gRPC進(jìn)階,gRPC請(qǐng)求的超時(shí)時(shí)間設(shè)置,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06源碼解析gtoken替換jwt實(shí)現(xiàn)sso登錄
這篇文章主要為大家介紹了源碼解析gtoken替換jwt實(shí)現(xiàn)sso登錄的示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06深入解析Go語(yǔ)言中crypto/subtle加密庫(kù)
本文主要介紹了深入解析Go語(yǔ)言中crypto/subtle加密庫(kù),詳細(xì)介紹crypto/subtle加密庫(kù)主要函數(shù)的用途、工作原理及實(shí)際應(yīng)用,具有一定的參考價(jià)值,感興趣的可以了解一下2024-02-02Go語(yǔ)言編程中判斷文件是否存在是創(chuàng)建目錄的方法
這篇文章主要介紹了Go語(yǔ)言編程中判斷文件是否存在是創(chuàng)建目錄的方法,示例都是使用os包下的函數(shù),需要的朋友可以參考下2015-10-10Go語(yǔ)言實(shí)現(xiàn)字符串切片賦值的方法小結(jié)
這篇文章主要給大家介紹了Go語(yǔ)言實(shí)現(xiàn)字符串切片賦值的兩種方法,分別是在for循環(huán)的range中以及在函數(shù)的參數(shù)傳遞中實(shí)現(xiàn),有需要的朋友們可以根據(jù)自己的需要選擇使用。下面來(lái)一起看看吧。2016-10-10