在Go中,变量何时变得无法访问? [英] In Go, when will a variable become unreachable?

查看:116
本文介绍了在Go中,变量何时变得无法访问?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Go 1.7 beta 1今天早上发布,这里是 Go 1.7的发行注记草稿

一>。一个新的函数 KeepAlive 被添加到包 runtime 中。
runtime.KeepAlive 给出了一个例子:

  type文件struct {d int} 
d,err:= syscall.Open( / file / path,syscall.O_RDONLY,0)
// ...做些事情如果错误!= nil ...
p:=& FILE {d}
runtime.SetFinalizer (p,func(p * File){syscall.Close(pd)})
var buf [10] byte $ b $,err:= syscall.Read(pd,buf [:])
//确保在读取返回之前p没有完成。
runtime.KeepAlive(p)
//在此之后没有更多的p使用。

runtime.SetFinalizer 的文档也给出了关于 runtime.KeepAlive d的结构体,并且p有一个终结器关闭那个文件描述符,如果
在函数中最后一次使用p是对syscall.Write(pd,buf,
size)的调用,那么一旦程序进入$,p可能无法访问b $ b syscall.Write。终结器可能会在那时运行,关闭pd,
导致syscall.Write失败,因为它正在写入一个已关闭的文件
描述符(或者更糟糕的是,导致完全不同的文件描述符打开
由不同的goroutine)。为了避免这个问题,在调用syscall.Write之后调用
runtime.KeepAlive(p)。

什么使我困惑变量 p 还没有离开它的生命范围,为什么它是不可达的?这是否意味着如果在下面的代码中没有使用变量,无论变量是否在其生命范围内,变量都将无法访问?

解决方案

当运行时检测到 Go 代码无法到达再次引用该变量的点时,变量变得无法访问。



在您发布的示例中, syscall.Open() 用于打开文件。返回的文件描述符(它只是一个 int 值)被包装在 struct 中。然后一个终结器被附加到这个结构值,该值关闭文件描述符。现在,当此结构值变得无法访问时,它的终结器可能在任何时候运行,并且关闭/无效/重新使用文件描述符可能会导致执行 Read()时出现意外的行为或错误。 系统调用。



最后一次使用此结构值 p 代码是当 syscall.Read()被调用(并且文件描述符 pd )被传递给它时)。系统调用的实现将在 syscall.Read()初始化之后使用该文件描述符,直到 syscall.Read()返回。但是这种文件描述符的使用与Go代码是独立的。



所以struct p 是在系统调用执行期间不使用,并且系统调用会阻止Go代码直到它返回。这意味着在执行 Read()时(在<$ c之前),允许Go运行时将 p 标记为无法访问$ c> read()返回),或者甚至在其实际执行开始之前(因为 p 仅用于提供参数来调用 Read()



因此,调用 runtime.KeepAlive() :因为此调用是在之后 syscall.Read()并且它引用变量 p ,在 Read()返回之前,Go运行时不允许标记 p 无法访问,因为这是在 Read() call。



请注意,您可以使用其他构造来保留 p alive,例如 _ = p 或者返回它。 runtime.KeepAlive()背景,它的实现是:

  func KeepAlive(interface {}){} 

runtime.KeepAlive()确实提供了更好的选择,因为:


  • 它清楚地表明我们希望保持 p alive(以防止运行 Finalizers )。

  • 使用其他的结构,比如 _ = p 可能会被未来的编译器优化,但不是 runtime.KeepAlive() / code>调用。


Go 1.7 beta 1 was released this morning, here is the release notes draft of Go 1.7. A new function KeepAlive was added to the package runtime. The doc of runtime.KeepAlive has given an example:

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 has also given an explanation about runtime.KeepAlive:

For example, if p points to a struct that contains a file descriptor d, and p has a finalizer that closes that file descriptor, and if the last use of p in a function is a call to syscall.Write(p.d, buf, size), then p may be unreachable as soon as the program enters syscall.Write. The finalizer may run at that moment, closing p.d, causing syscall.Write to fail because it is writing to a closed file descriptor (or, worse, to an entirely different file descriptor opened by a different goroutine). To avoid this problem, call runtime.KeepAlive(p) after the call to syscall.Write.

What confused me is that the variable p has not left its life scope yet, why will it be unreachable? Does that mean that a variable will be unreachable if only there is no use of it in the following code, no matter whether it is in its life scope?

解决方案

A variable becomes unreachable when the runtime detects that the Go code cannot reach a point where that variable is referenced again.

In the example you posted, a syscall.Open() is used to open a file. The returned file descriptor (which is just an int value) is "wrapped" in a struct. Then a finalizer is attached to this struct value that closes the file descriptor. Now when this struct value becomes unreachable, its finalizer may be run at any moment, and the closing / invalidation / re-using of the file descriptor could cause unexpected behavior or errors in the execution of the Read() syscall.

The last use of this struct value p in Go code is when syscall.Read() is invoked (and the file descriptor p.d is passed to it). The implementation of the syscall will use that file descriptor after the initiation of syscall.Read(), it may do so up until syscall.Read() returns. But this use of the file descriptor is "independent" of the Go code.

So the struct value p is not used during the execution of the syscall, and the syscall blocks the Go code until it returns. Which means the Go runtime is allowed to mark p as unreachable during the execution of Read() (before Read() returns), or even before its actual execution begins (because p is only used to provide the arguments to call Read().

Hence the call to runtime.KeepAlive(): since this call is after the syscall.Read() and it references the variable p, the Go runtime is not allowed to mark p unreachable before Read() returns, because this is after the Read() call.

Note that you could use other constructs to "keep p alive", e.g. _ = p or returning it. runtime.KeepAlive() does nothing magical in the background, its implementation is:

func KeepAlive(interface{}) {}

runtime.KeepAlive() does provide a much better alternative because:

  • It clearly documents we want to keep p alive (to prevent runs of Finalizers).
  • Using other constructs such as _ = p might get "optimized" out by future compilers, but not runtime.KeepAlive() calls.

这篇关于在Go中,变量何时变得无法访问?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆