Go 1.7 beta 1今天早上发布,现在是the release notes draft of Go 1.7。新功能KeepAlive
已添加到包runtime
。 The doc of runtime.KeepAlive
举了一个例子:
type File struct { d int }
d, err := syscall.Open("/file/path", syscall.O_RDONLY, 0)
// ... do something if err != nil ...
p := &FILE{d}
runtime.SetFinalizer(p, func(p *File) { syscall.Close(p.d) })
var buf [10]byte
n, err := syscall.Read(p.d, buf[:])
// Ensure p is not finalized until Read returns.
runtime.KeepAlive(p)
// No more uses of p after this point.
The doc of runtime.SetFinalizer
也对runtime.KeepAlive
:
例如,如果p指向包含文件描述符的结构 d,并且p有一个关闭该文件描述符的终结器,如果是 最后在函数中使用p是对syscall.Write的调用(p.d,buf, 大小),一旦程序进入,p可能无法访问 syscall.Write。终结者可以在那一刻运行,关闭p.d, 导致syscall.Write失败,因为它正在写入已关闭的文件 描述符(或者更糟糕的是,打开了一个完全不同的文件描述符 由不同的goroutine)。要避免此问题,请致电 调用syscall.Write之后运行时.KeepAlive(p)。
令我感到困惑的是,变量p
尚未离开其生命范围,为什么它无法到达?这是否意味着如果变量在以下代码中没有使用它将无法访问,无论它是否在其生命范围内?
答案 0 :(得分:9)
当运行时检测到 Go 代码无法到达再次引用该变量的点时,变量无法访问。
在您发布的示例中,syscall.Open()
用于打开文件。返回的文件描述符(只是int
值)是"包装"在struct
中。然后将一个终结器附加到此结构值,该结构值将关闭文件描述符。现在,当此结构值变得无法访问时,其终结器可能随时运行,并且文件描述符的关闭/无效/重新使用可能会导致Read()
系统调用执行中的意外行为或错误。
在 Go 代码中最后一次使用此结构值p
是在调用syscall.Read()
时(文件描述符p.d
被传递给它)。系统调用的实现将在syscall.Read()
的启动之后使用该文件描述符,它可以在syscall.Read()
返回之前执行。但是这种文件描述符的使用是"独立的" Go代码。
因此在执行syscall期间不使用结构值p
,并且syscall会阻止Go代码,直到它返回为止。这意味着允许Go运行时在执行p
期间(Read()
返回之前)将Read()
标记为无法访问,或者甚至在其实际执行开始之前(因为p
仅用于提供调用Read()
的参数。
因此调用runtime.KeepAlive()
:因为此调用是之后 syscall.Read()
而引用变量p
,在p
返回之前,不允许Go运行时标记Read()
无法访问,因为这是在Read()
调用之后。
请注意,您可以使用其他构造来保持p
活着",例如_ = p
或将其归还。 runtime.KeepAlive()
在背景中没有任何神奇之处,它的实现是:
func KeepAlive(interface{}) {}
runtime.KeepAlive()
提供了更好的选择,因为:
p
保持活跃的文档(以防止Finalizers的运行。)_ = p
之类的其他结构可能会得到优化"未来的编译器,但不是runtime.KeepAlive()
来电。