作为go例程调用函数会产生不同于作为匿名函数调用go例程的调用堆栈
我有一个名为PrintCaller()的函数,它调用runtime.Caller()并跳过一帧以获取和打印调用方(PrintCaller的)文件名和行号。当以同步方式运行时,以及如果以匿名函数的形式异步调用时,这将按预期工作。但是,如果仅使用作为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
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
- 但当你写作时:
您正在从特定于计算机的goroutine启动代码调用名为go printCaller()
的函数printCaller
printCaller
打印其调用方的名称,这是特定于机器的启动代码,因此您可以看到
这里有一个很大的警告,那就是
运行时。允许调用方失败。这就是它返回布尔值ok
以及pcuintpttr、文件字符串、行int
值的原因。无法保证能够找到特定于计算机的程序集调用者。调用者是堆栈跟踪中显示的汇编语言函数。goroutine就是这样被调用的:有一个程序集存根调用它们。当您定义一个调用函数的闭包并使用go-func(){…}
调用该函数时,调用闭包的存根是同一个存根,func(){…}
,但是现在您的闭包调用您的PrintCaller
,因此您看到的调用方就是闭包。“有没有办法使用go-PrintCaller()实现这一点?”不。@torek说得通,谢谢你的解释!请随意添加这个作为答案,我会将其标记为已回答。