如何获取goroutine的运行时ID?

huangapple go评论81阅读模式
英文:

How can I get a goroutine's runtime ID?

问题

如何获取goroutine的运行时ID?

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

我找到了一些关于runtime.GoID的参考:

func worker() {
    id := runtime.GoID()
    log.Println("Goroutine ID:", id)
}

但是看起来这个方法现在已经过时/已被移除 - https://pkg.go.dev/runtime?

英文:

How can I get a goroutine's runtime ID?

I'm getting interleaved logs from an imported package - one approach would be to add a unique identifier to the logs of each goroutine.

I've found some references to runtime.GoID:

func worker() {
    id := runtime.GoID()
    log.Println("Goroutine ID:", id)
}

But it looks like this is now outdated/has been removed - https://pkg.go.dev/runtime?

答案1

得分: 5

Go语言有意选择不提供goroutine的ID,因为这样会鼓励编写更糟糕的软件,并对整个生态系统造成伤害。如果需要唯一标识符,应该将其作为参数传递给函数,或者通过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]))
}

示例输出:

1 <nil>
19 <nil>
18 <nil>

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

英文:

Go deliberately chooses not to provide an ID since it would encourage worse software and hurt the overall ecosystem: https://go.dev/doc/faq#no_goroutine_id

Generally, the desire to de-anonymize goroutines is a design flaw and is strongly not recommended. There is almost always going to be a much better way to solve the issue at hand. Eg, if you need a unique identifier, it should be passed into the function or potentially via context.Context.

However, internally the runtime needs IDs for the implementation. For educational purposes you can find them with something like:

package main

import (
    &quot;bytes&quot;
    &quot;errors&quot;
    &quot;fmt&quot;
    &quot;runtime&quot;
    &quot;strconv&quot;
)

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

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

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

// 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, &#39; &#39;)
    if i &lt; 0 {
        return 0, errBadStack
    }

    return strconv.Atoi(string(buf[:i]))
}

Example output:

1 &lt;nil&gt;
19 &lt;nil&gt;
18 &lt;nil&gt;

They can also be found (less portably) via assembly by accessing the goid field in the g struct. This is how packages like github.com/petermattis/goid typically do it.

huangapple
  • 本文由 发表于 2023年2月6日 20:09:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/75361134.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定