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

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

问题描述

Go 1.7 beta 1 今天早上发布,这里是 Go 1.7 的发行说明草稿.一个新函数 KeepAlive 被添加到包 runtime 中.runtime.KeepAlive的文档给出了一个例子:

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.

runtime.SetFinalizer的文档也有给出了关于runtime.KeepAlive的解释:

例如,如果 p 指向一个包含文件描述符的结构d,并且 p 有一个关闭该文件描述符的终结器,如果在函数中最后一次使用 p 是调用 syscall.Write(p.d, buf,size),那么 p 可能一进入程序就无法到达系统调用.写.终结器可能会在那个时候运行,关闭 p.d,导致 syscall.Write 失败,因为它正在写入已关闭的文件描述符(或者,更糟糕的是,打开一个完全不同的文件描述符由不同的 goroutine).为避免此问题,请致电runtime.KeepAlive(p) 在调用 syscall.Write 之后.

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.

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

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?

推荐答案

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

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

在您发布的示例中,一个 syscall.Open() 用于打开文件.返回的文件描述符(只是一个 int 值)被包装"在一个 struct 中.然后一个终结器附加到这个结构值上,关闭文件描述符.现在当这个结构值变得不可访问时,它的终结器可能随时运行,并且文件描述符的关闭/失效/重用可能会导致 Read() 系统调用.

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.

Go 代码中最后一次使用这个结构值 p 是在调用 syscall.Read() 时(以及文件描述符 pd 被传递给它).系统调用的实现将在 syscall.Read()initiation 之后使用该文件描述符,它可能会一直使用到 syscall.Read()返回.但是文件描述符的这种使用是独立"于 Go 代码的.

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.

所以结构值p在系统调用的执行过程中没有被使用,系统调用阻塞了Go代码直到它返回.这意味着允许 Go 运行时在 Read() 执行期间(在 Read() 返回之前)将 p 标记为不可访问,或者 甚至在其实际执行开始之前(因为 p 仅用于提供调用 Read() 的参数.

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().

因此调用 runtime.KeepAlive():因为这个调用是 syscall.Read() 并且它引用 变量p,Go在 Read() 返回之前,运行时不允许将 p 标记为不可访问,因为这是在 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.

请注意,您可以使用其他构造来保持 p 活着",例如_ = p 或返回它.runtime.KeepAlive() 在后台没有做任何神奇的事情,它的实现是:

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() 确实提供了更好的选择,因为:

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

  • 它清楚地记录了我们希望保持 p 存活(以防止 终结者).
  • 使用 _ = p 等其他构造可能会被未来的编译器优化"出来,但不会被 runtime.KeepAlive() 调用.
  • 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天全站免登陆