如何获取 Goroutine 的运行时 ID?

hit*_*lin 4 concurrency logging runtime go

如何获取 Goroutine 的运行时 ID?

我从导入的包中获取交错日志 - 一种方法是向每个 goroutine 的日志添加唯一标识符。

我找到了一些参考资料runtime.GoID

func worker() {
    id := runtime.GoID()
    log.Println("Goroutine ID:", id)
}
Run Code Online (Sandbox Code Playgroud)

但看起来现在已经过时/已被删除 - https://pkg.go.dev/runtime

mpx*_*mpx 9

Go 故意选择不提供 ID,因为这会鼓励更糟糕的软件并损害整个生态系统:https://go.dev/doc/faq#no_goroutine_id

一般来说,去匿名化 goroutine 的愿望是一个设计缺陷,强烈不建议这样做。几乎总会有更好的方法来解决手头的问题。例如,如果您需要唯一标识符,则应将其传递到函数中或可能通过 context.Context 传递。

然而,运行时内部需要 ID 来实现。出于教育目的,您可以通过以下方式找到它们:

package main

import (
    "bytes"
    "errors"
    "fmt"
    "runtime"
    "strconv"
)

func main() {
    fmt.Println(goid())

    done := make(chan struct{})
    go func() {
        fmt.Println(goid())
        done <- struct{}{}
    }()
    go func() {
        fmt.Println(goid())
        done <- struct{}{}
    }()
    <-done
    <-done
}

var (
    goroutinePrefix = []byte("goroutine ")
    errBadStack     = errors.New("invalid runtime.Stack output")
)

// This is terrible, slow, and should never be used.
func goid() (int, error) {
    buf := make([]byte, 32)
    n := runtime.Stack(buf, false)
    buf = buf[:n]
    // goroutine 1 [running]: ...

    buf, ok := bytes.CutPrefix(buf, goroutinePrefix)
    if !ok {
        return 0, errBadStack
    }

    i := bytes.IndexByte(buf, ' ')
    if i < 0 {
        return 0, errBadStack
    }

    return strconv.Atoi(string(buf[:i]))
}
Run Code Online (Sandbox Code Playgroud)

输出示例:

1 <nil>
19 <nil>
18 <nil>
Run Code Online (Sandbox Code Playgroud)

goid它们也可以通过访问结构中的字段来通过汇编找到(不太可移植)g。这就是像github.com/petermattis/goid这样的包通常的做法。