【问题标题】:In Go, when will a variable become unreachable?在 Go 中,变量何时会变得不可访问?
【发布时间】:2016-06-02 10:01:13
【问题描述】:

今天早上发布了 Go 1.7 beta 1,这里是 the release notes draft of Go 1.7。一个新函数KeepAlive 被添加到包runtime 中。 The doc of 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.

The doc of 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 之后。

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

【问题讨论】:

    标签: go garbage-collection


    【解决方案1】:

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

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

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

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

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

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

    func KeepAlive(interface{}) {}
    

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

    • 它清楚地记录了我们希望保持p 存活(以防止Finalizers 运行)。
    • 使用其他构造(例如 _ = p)可能会被未来的编译器“优化”,但不会被 runtime.KeepAlive() 调用。

    【讨论】:

      猜你喜欢
      • 2014-05-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-07
      • 2023-03-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多