在 Go 中,变量何时会变得不可访问? [英] In Go, when will a variable become unreachable?
问题描述
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 notruntime.KeepAlive()
calls.
这篇关于在 Go 中,变量何时会变得不可访问?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!