将函数作为 go routine 调用会产生与 go routine 不同的调用堆栈作为匿名 func
Calling a function as a go routine produces different call stack from go routine as anonymous func
我有一个名为 PrintCaller() 的函数,它调用 runtime.Caller() 并跳过一帧以获取并打印调用者(PrintCaller 的)文件名和行号。这在 运行 同步时按预期工作,如果作为匿名函数异步调用。但是,如果 运行 仅使用 go
关键字,则调用者的堆栈帧将替换为一些内部函数调用。
例如,这是函数:
func printCaller(wait chan bool) {
_, fileName, line, _ := runtime.Caller(1)
fmt.Printf("Filename: %s, line: %d\n", fileName, line)
}
如果我调用是这样的:
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
这里的工作示例:https://play.golang.org/p/Jv21SVDY2Ln
为什么我调用 go PrintCaller()
时会发生这种情况,而调用 go func(){PrintCaller()}()
时却不会?另外,有什么方法可以使用 go PrintCaller()
来完成这项工作吗?
鉴于 Go 运行时系统的内部工作原理,您所看到的输出是人们所期望的:
一个goroutine,比如在package main
中调用你自己的main
的主goroutine,还包括go somefunc()
启动的routines,实际上是调用来自 一些特定于机器的启动例程。在 src/runtime/asm_amd64.s
.
的操场上
定义闭包时,如:
f := func() {
// code
}
这将创建一个匿名函数。调用它:
f()
调用那个匿名函数,不管调用者是谁。无论闭包是分配给变量(如上面的 f
)还是立即调用,或稍后使用 defer
或其他任何方式调用,都是如此:
defer func() {
// code ...
}()
所以,写作:
go func() {
// code ...
}()
只是从同一个特定于机器的启动中调用此处的匿名函数。如果 那个函数 然后调用你的 printCaller
函数,它使用 runtime.Caller(1)
跳过你的 printCaller
函数并找到它的调用者,它找到匿名函数:
Filename: /tmp/sandbox297971268/prog.go, line: 22
例如。
但是当你写的时候:
go printCaller()
您正在从特定于机器的 goroutine 启动代码中调用名为 printCaller
的函数。
由于 printCaller
打印其调用者的名称,即此机器特定的启动代码,这就是您所看到的。
这里有一个很大的警告,那就是 runtime.Caller
允许失败。这就是为什么它 returns 一个布尔值 ok
以及 pc uintptr, file string, line int
值。不能保证可以找到特定于机器的程序集调用程序。
我有一个名为 PrintCaller() 的函数,它调用 runtime.Caller() 并跳过一帧以获取并打印调用者(PrintCaller 的)文件名和行号。这在 运行 同步时按预期工作,如果作为匿名函数异步调用。但是,如果 运行 仅使用 go
关键字,则调用者的堆栈帧将替换为一些内部函数调用。
例如,这是函数:
func printCaller(wait chan bool) {
_, fileName, line, _ := runtime.Caller(1)
fmt.Printf("Filename: %s, line: %d\n", fileName, line)
}
如果我调用是这样的:
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
这里的工作示例:https://play.golang.org/p/Jv21SVDY2Ln
为什么我调用 go PrintCaller()
时会发生这种情况,而调用 go func(){PrintCaller()}()
时却不会?另外,有什么方法可以使用 go PrintCaller()
来完成这项工作吗?
鉴于 Go 运行时系统的内部工作原理,您所看到的输出是人们所期望的:
一个goroutine,比如在package
的操场上main
中调用你自己的main
的主goroutine,还包括go somefunc()
启动的routines,实际上是调用来自 一些特定于机器的启动例程。在src/runtime/asm_amd64.s
.定义闭包时,如:
f := func() { // code }
这将创建一个匿名函数。调用它:
f()
调用那个匿名函数,不管调用者是谁。无论闭包是分配给变量(如上面的
f
)还是立即调用,或稍后使用defer
或其他任何方式调用,都是如此:defer func() { // code ... }()
所以,写作:
go func() { // code ... }()
只是从同一个特定于机器的启动中调用此处的匿名函数。如果 那个函数 然后调用你的
printCaller
函数,它使用runtime.Caller(1)
跳过你的printCaller
函数并找到它的调用者,它找到匿名函数:Filename: /tmp/sandbox297971268/prog.go, line: 22
例如。
但是当你写的时候:
go printCaller()
您正在从特定于机器的 goroutine 启动代码中调用名为
printCaller
的函数。
由于 printCaller
打印其调用者的名称,即此机器特定的启动代码,这就是您所看到的。
这里有一个很大的警告,那就是 runtime.Caller
允许失败。这就是为什么它 returns 一个布尔值 ok
以及 pc uintptr, file string, line int
值。不能保证可以找到特定于机器的程序集调用程序。