Go 1.7 beta 1今天早上发布,这是Go 1.7的发行说明草案。新功能KeepAlive已添加到程序包中runtime。的文档runtime.KeepAlive给出了一个示例:
KeepAlive
runtime
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.
的文档runtime.SetFinalizer还给出了以下解释runtime.KeepAlive:
runtime.SetFinalizer
例如,如果p指向包含文件描述符d的结构,并且p具有关闭该文件描述符的终结器,并且函数中对p的最后使用是对syscall的调用。Write(pd,buf,size ),则一旦程序进入syscall.Write,p可能无法访问。终结器可能会在此时运行,关闭pd,导致syscall.Write失败,因为它正在写入关闭的文件描述符(或更糟的是,写入由不同的goroutine打开的完全不同的文件描述符)。为避免此问题,请在调用syscall.Write之后调用runtime.KeepAlive(p)。
让我感到困惑的是,该变量p还没有离开它的生存范围,为什么它不可达?这是否意味着如果在以下代码中仅使用变量,则无论变量是否在其生存范围内,都将无法访问该变量?
p
当运行时检测到 Go 代码无法到达再次引用该变量的位置时,变量将变得不可访问。
在您发布的示例中,a syscall.Open()用于打开文件。返回的文件描述符(只是一个int值)被“包装”在struct。然后,将终结器附加到此结构值,以关闭文件描述符。现在,当此结构值变得不可访问时,其终结器可能随时运行,并且文件描述符的关闭/失效/重用可能会导致意外的行为或Read()系统调用执行中的错误。
syscall.Open()
int
struct
Read()
在一次使用这种结构值的p在 围棋 代码时syscall.Read()被调用(和文件描述符p.d传递给它)。系统调用的实现将使用文件描述符后 开始 的syscall.Read(),它可以这样做,直到syscall.Read()回报。但是,文件描述符的这种使用与Go代码“无关”。
syscall.Read()
p.d
因此,p在执行系统调用期间不会使用struct值,并且系统调用会阻塞Go代码,直到返回为止。这意味着Go运行时p在执行过程中Read()(Read()返回之前)或 什至在其实际执行开始之前 被标记为不可访问(因为p仅用于提供call的参数)Read()。
因此,对runtime.KeepAlive():的调用由于在该调用 之后 ,syscall.Read()并且它 引用 了变量p,因此不允许Go运行时p在Read()返回之前将其标记为不可访问,因为在Read()调用之后。
runtime.KeepAlive()
请注意,您可以使用其他构造来“保持p活动”,例如_ = p返回它。runtime.KeepAlive()在后台没有任何神奇的事情,其实现是:
_ = p
func KeepAlive(interface{}) {}
runtime.KeepAlive() 确实提供了更好的选择,因为: