作为go例程调用函数会产生不同于作为匿名函数调用go例程的调用堆栈

作为go例程调用函数会产生不同于作为匿名函数调用go例程的调用堆栈,go,runtime,goroutine,callstack,Go,Runtime,Goroutine,Callstack,我有一个名为PrintCaller()的函数,它调用runtime.Caller()并跳过一帧以获取和打印调用方(PrintCaller的)文件名和行号。当以同步方式运行时,以及如果以匿名函数的形式异步调用时,这将按预期工作。但是,如果仅使用go关键字运行,则调用方的堆栈框架将替换为一些内部函数调用 例如,这是一个函数: func printCaller(wait chan bool) { _, fileName, line, _ := runtime.Caller(1) fmt

我有一个名为PrintCaller()的函数,它调用runtime.Caller()并跳过一帧以获取和打印调用方(PrintCaller的)文件名和行号。当以同步方式运行时,以及如果以匿名函数的形式异步调用时,这将按预期工作。但是,如果仅使用
go
关键字运行,则调用方的堆栈框架将替换为一些内部函数调用

例如,这是一个函数:

func printCaller(wait chan bool) {
    _, fileName, line, _ := runtime.Caller(1)
    fmt.Printf("Filename: %s, line: %d\n", fileName, line)
}
Filename: /tmp/sandbox297971268/prog.go, line: 22
如果我这样打电话:

func main() {
    printCaller()
    go func(){printCaller()}()
    go printCaller()
}
输出为:

Filename: /tmp/sandbox297971268/prog.go, line: 19
Filename: /tmp/sandbox297971268/prog.go, line: 22
Filename: /usr/local/go-faketime/src/runtime/asm_amd64.s, line: 1374
此处的工作示例:


为什么在调用
go-PrintCaller()
时会发生这种情况,而在调用
go-func(){PrintCaller()}()
时不会发生这种情况?另外,是否有任何方法可以使用go PrintCaller()?

鉴于go运行时系统的内部工作原理,您看到的输出是预期的:

  • 一个goroutine,例如在包
    main
    中调用您自己的
    main
    ,但也包括由
    go somefunc()
    启动的例程的主例程,实际上是从某些特定于机器的启动例程中调用的。在操场上,这是
    src/runtime/asm_amd64.s
    中的一个

  • 定义闭包时,例如:

    f := func() {
        // code
    }
    
    这将创建一个匿名函数。称之为:

    f()
    
    从任何调用方调用该匿名函数。无论是将闭包分配给变量(如上面的
    f
    ),还是立即调用,或稍后使用
    defer
    ,或其他任何方式调用,这都是正确的:

    defer func() {
        // code ...
    }()
    
  • 因此,写:

    go func() {
        // code ...
    }()
    
    只需从同一台特定于机器的启动调用此处的匿名函数。如果该函数随后调用您的
    printCaller
    函数,该函数使用
    runtime.Caller(1)
    跳过您的
    printCaller
    函数并查找其调用者,它将查找匿名函数:

    func printCaller(wait chan bool) {
        _, fileName, line, _ := runtime.Caller(1)
        fmt.Printf("Filename: %s, line: %d\n", fileName, line)
    }
    
    Filename: /tmp/sandbox297971268/prog.go, line: 22
    
    比如说

  • 但当你写作时:

    go printCaller()
    
    您正在从特定于计算机的goroutine启动代码调用名为
    printCaller
    的函数

由于
printCaller
打印其调用方的名称,这是特定于机器的启动代码,因此您可以看到


这里有一个很大的警告,那就是
运行时。允许调用方失败。这就是它返回布尔值
ok
以及
pcuintpttr、文件字符串、行int
值的原因。无法保证能够找到特定于计算机的程序集调用者。

调用者是堆栈跟踪中显示的汇编语言函数。goroutine就是这样被调用的:有一个程序集存根调用它们。当您定义一个调用函数的闭包并使用
go-func(){…}
调用该函数时,调用闭包的存根是同一个存根,
func(){…}
,但是现在您的闭包调用您的
PrintCaller
,因此您看到的调用方就是闭包。“有没有办法使用go-PrintCaller()实现这一点?”不。@torek说得通,谢谢你的解释!请随意添加这个作为答案,我会将其标记为已回答。