package main
import (
"sync"
"runtime"
)
type S struct {
chs chan int
}
var wg sync.WaitGroup
func worker(s *S) {
for i := range s.chs {
println("In worker, ch = ", i)
}
wg.Done()
}
func main() {
s := S{make(chan int)}
runtime.SetFinalizer(&s, func(ss *S) {
println("Finalizer")
close(ss.chs)
})
wg.Add(1)
go worker(&s)
for i := 0; i < 1; i++ {
s.chs <- 1
}
runtime.GC()
wg.Wait()
}
输出(转1.8.3):
在工人中,ch = 1
终结
我希望这个程序陷入僵局。 runtime.GC()
不会收集s
,因为worker()
包含对s.chs
的引用。
然而,它以第1.8.3节结束。在s
的终结器中,甚至close(s.chs)
成功调用。
我想知道它是否与range
和GC有关。
非常感谢。
答案 0 :(得分:1)
我不是100%确定这是发生了什么,而是来自runtime
godoc,SetFinalizer
的第二段到最后一段:
例如,如果p指向包含文件描述符的结构 d,并且p有一个关闭该文件描述符的终结器,如果是 最后在函数中使用p是对syscall.Write的调用(p.d,buf, 大小),一旦程序进入,p可能无法访问 syscall.Write。终结者可以在那一刻运行,关闭p.d, 导致syscall.Write失败,因为它正在写入已关闭的文件 描述符(或者更糟糕的是,打开了一个完全不同的文件描述符 由不同的goroutine)。要避免此问题,请致电 调用syscall.Write之后运行时.KeepAlive(p)。
对我来说,Finalizer
可能会在最后一次使用var
之后立即运行。
我自己从来没有想过这种可能性,但从技术上讲,GC可以知道它是否可以在变量范围消失之前收集变量。考虑这个简单的例子。
func main() {
str := "Hello World"
fmt.Println(str)
someMainLoop()
// nothing after this, but someMainLoop() continues until stopped manually
}
GC无法收集str
没有理由。它永远不会再次使用,它很有可能知道这一点。