Golang中trace包是如何为goroutine分配ID的?

Golang中trace包是如何为goroutine分配ID的? 问题较长

我试图理解Go的trace包是如何提取并分配goroutine各自ID的。 我使用了这里展示的代码。 运行此代码后,我执行以下命令:

curl localhost:1328/debug/pprof/trace?seconds=10 > trace.out
go tool trace trace.out

我得到如下输出:

Opening browser. Trace viewer is listening on http://127.0.0.1:xxxx

这会运行并提供一个GUI界面,我可以在其中看到所有被调用的方法及其相应的跟踪字段和值。其中有一个部分是Goroutine分析,当我点击该链接时,此链接中的代码会被执行。

通过修改源代码来尝试理解如何获取每个goroutine的ID后,我仍然无法理解这段代码。

我能够打印出在此路由上运行的ID。但它是如何实际存储这些ID的,基于什么标准?我知道这发生在这里的某个地方。

任何帮助都将不胜感激。谢谢。


更多关于Golang中trace包是如何为goroutine分配ID的?的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang中trace包是如何为goroutine分配ID的?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go的trace包中,goroutine ID的分配和跟踪是通过运行时内部的goid实现的,但trace包并不直接分配ID,而是记录运行时已存在的goroutine ID。以下是关键机制:

  1. 运行时goroutine ID:每个goroutine在创建时由运行时分配唯一的goid(64位整数),存储在g结构体中。trace记录的是这个现有ID。

  2. trace事件捕获:当启用trace时,运行时在特定事件(如goroutine创建、切换、销毁)时记录事件,其中包含goid。例如:

    // 运行时内部记录goroutine创建事件
    func traceGoCreate(newg *g, pc uintptr) {
        // 记录事件类型EvGoCreate,包含newg.goid
    }
    
  3. trace解析:在解析trace文件时,internal/trace包通过事件流重建goroutine信息。关键代码在goroutines.goprocessEvent函数中:

    func (g *goroutine) processEvent(ev *Event, inSlidingWindow bool) {
        switch ev.Type {
        case EvGoCreate:
            // 从事件中提取goid
            g.ID = ev.Args[0] // 第一个参数就是goroutine ID
            g.CreationTime = ev.Ts
        }
    }
    
  4. ID存储标准:trace文件中的goroutine ID直接来自运行时的goid分配,标准是:

    • 单调递增:运行时使用全局计数器sched.goidgen分配ID
    • 唯一性:每个goroutine生命周期内ID不变
    • 回收利用:已销毁goroutine的ID可能被新goroutine重用
  5. 示例验证:可以通过以下代码验证trace中的goroutine ID:

    package main
    
    import (
        "fmt"
        "runtime/trace"
        "os"
        "sync"
    )
    
    func main() {
        f, _ := os.Create("trace.out")
        trace.Start(f)
        defer trace.Stop()
        
        var wg sync.WaitGroup
        for i := 0; i < 3; i++ {
            wg.Add(1)
            go func(id int) {
                defer wg.Done()
                // 运行时分配的goid可通过debug.Stack获取
                // 但trace会记录这个固有ID
                fmt.Printf("Goroutine %d executing\n", id)
            }(i)
        }
        wg.Wait()
    }
    

运行后解析trace文件,你会看到每个goroutine都有唯一的ID,这些ID对应运行时分配的goid。trace包只是捕获并展示这些固有ID,而非重新分配。

回到顶部