3.8.2 纯Go方式获取goid
为了便于理解,我们先尝试用纯Go的方式获取goid。使用纯Go的方式获取goid的方式虽然性能较低,但是代码有着很好的移植性,同时也可以用于测试验证其它方式获取的goid是否正确。
每个Go语言用户应该都知道panic函数。调用panic函数将导致Goroutine异常,如果panic在传递到Goroutine的根函数还没有被recover函数处理掉,那么运行时将打印相关的异常和栈信息并退出Goroutine。
下面我们构造一个简单的例子,通过panic来输出goid:
package main
func main() {
panic("goid")
}
运行后将输出以下信息:
panic: goid
goroutine 1 [running]:
main.main()
/path/to/main.go:4 +0x40
我们可以猜测Panic输出信息goroutine 1 [running]
中的1就是goid。但是如何才能在程序中获取panic的输出信息呢?其实上述信息只是当前函数调用栈帧的文字化描述,runtime.Stack函数提供了获取该信息的功能。
我们基于runtime.Stack函数重新构造一个例子,通过输出当前栈帧的信息来输出goid:
package main
import "runtime"
func main() {
var buf = make([]byte, 64)
var stk = buf[:runtime.Stack(buf, false)]
print(string(stk))
}
运行后将输出以下信息:
goroutine 1 [running]:
main.main()
/path/to/main.g
因此从runtime.Stack获取的字符串中就可以很容易解析出goid信息:
func GetGoid() int64 {
var (
buf [64]byte
n = runtime.Stack(buf[:], false)
stk = strings.TrimPrefix(string(buf[:n]), "goroutine ")
)
idField := strings.Fields(stk)[0]
id, err := strconv.Atoi(idField)
if err != nil {
panic(fmt.Errorf("can not get goroutine id: %v", err))
}
return int64(id)
}
GetGoid函数的细节我们不再赘述。需要补充说明的是runtime.Stack
函数不仅仅可以获取当前Goroutine的栈信息,还可以获取全部Goroutine的栈信息(通过第二个参数控制)。同时在Go语言内部的 net/http2.curGoroutineID 函数正是采用类似方式获取的goid。
学员评价