go - 将函数作为 go 例程调用会产生与 go 例程不同的调用堆栈作为匿名 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,例如
main
在 package中调用你自己的 goroutine,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
函数并找到它的调用者,它会找到匿名函数:Filename: /tmp/sandbox297971268/prog.go, line: 22
例如。
但是当你写:
go printCaller()
您正在调用
printCaller
从特定于机器的 goroutine 启动代码命名的函数。
由于printCaller
打印了它的调用者的名字,这是这个机器特定的启动代码,这就是你所看到的。
这里有一个很大的警告,那runtime.Caller
就是允许失败。这就是它返回布尔值ok
和pc uintptr, file string, line int
值的原因。不能保证特定于机器的程序集调用者可以找到。
推荐阅读
- c# - 使用 Microsoft.Office.Interop.Excel 打开 excel 文件的应用程序与服务器上的 RDP 会话冲突
- android - 从我的片段中隐藏浮动操作按钮,即使我改变手机的方向也保持隐藏
- linux - FFmpeg — 尝试替换音频时出现分段错误
- r-plotly - 如何强制 plotly R 用类别轴绘制缺失值
- kubernetes - 使用 kubernetes 将密钥附加到 CATALINA_OPTS
- php - PHP:“无法获取 DOMElement。节点不再存在于”使用 DOMXpath
- tensorflow-data-validation - 如何以正确的格式保存 TFDV 统计信息以便重新加载?
- java - 动画 Drawable - 空对象引用
- bash - 在文件开头添加 0 以获得 5 位组
- docker - 为什么 cap_net_bind_service 在这个 nginx 嵌套 Docker 容器中不起作用?