doujuncuo9339 2016-06-02 10:01
浏览 7
已采纳

在Go中,变量何时将变得不可访问?

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?

  • 写回答

1条回答 默认 最新

  • dpzlz08480 2016-06-02 12:00
    关注

    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.
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 各位请问平行检验趋势图这样要怎么调整?说标准差差异太大了
  • ¥15 delphi webbrowser组件网页下拉菜单自动选择问题
  • ¥15 wpf界面一直接收PLC给过来的信号,导致UI界面操作起来会卡顿
  • ¥15 init i2c:2 freq:100000[MAIXPY]: find ov2640[MAIXPY]: find ov sensor是main文件哪里有问题吗
  • ¥15 运动想象脑电信号数据集.vhdr
  • ¥15 三因素重复测量数据R语句编写,不存在交互作用
  • ¥15 微信会员卡等级和折扣规则
  • ¥15 微信公众平台自制会员卡可以通过收款码收款码收款进行自动积分吗
  • ¥15 随身WiFi网络灯亮但是没有网络,如何解决?
  • ¥15 gdf格式的脑电数据如何处理matlab