//通过学习我们了解了defer函数的各种特性,尤其是与匿名函数的结合使用。
但是在go程序设计语言一书中 关于defer元素处理文件导致用尽所有文件描述符号的问题,一直不能很好的理解。
网上找了很多笔记,感觉都跟书上一样描述的比较模糊,顾对这段内容进行尝试:(如不想看如此繁琐的过程,可直接跳到最后的结论去。)
原文内容:
因为延迟的函数不到函数的最后一刻是不执行的,要注意循环里的defer语句的使用下面的这段代码就可能会用尽所有的文件描述符号,这是因为处理完成后却没有文件的关闭。
func Open(filenames []string)error{
for _,filename:=range filenames{
file, err := os.Open(filename)
if err!=nil {
return err
}
defer file.Close()
//do something
}
}
盲点: go语言的for range循环,跟其他语言的foreach 比起来不同点是其接受到的迭代变量是一个值类型,而不是一个引用类型。(或者说这个值类型是在循环中固定的接收地址)
有人说java 的foreach 对值类型的迭代依然是值类型,但是内存关系其相当于是在循环语句外面创建传入循环的。
因为这种差异顾进行了各种尝试。
尝试1:为什么可能出现文件处理完之后完全没有关闭。
func main(){
filenames :=[]string{"/media","/etc","/opt"}
group := sync.WaitGroup{}
for _,filename:=range filenames{
group.Add(1)
defer DoneWg(&group,filename)
time.Sleep(1*time.Second)
}
group.Wait()
}
func DoneWg(wg *sync.WaitGroup,str string) {
fmt.Println(str+"---DoneClose")
wg.Done()
}
运行结果如下:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [semacquire]:
sync.runtime_Semacquire(0xc0000160f8)
/usr/go/src/runtime/sema.go:56 +0x39
sync.(*WaitGroup).Wait(0xc0000160f0)
/usr/go/src/sync/waitgroup.go:130 +0x65
main.main()
/media/wanglili/willinggo/golang/src/part_5_digui/defer_nonefunc.go:49 +0x11b
尝试1得出结论: 在for执行defer函数也是在循环外层结束之后才执行。
尝试2:什么原因可能导致defer 不能被执行。困惑在语言中的理解所有的defer一定会不执行,就算被panic的打断的也会执行defer函数,如实做了各种集中尝试都几乎都可以让defer被执行。
除了如下几种方式:
1:程序退出
filenames :=[]string{"/home/wanglili/xxxxxxxxxxxx"}
group := sync.WaitGroup{}
for _,ti:=range filenames{
group.Add(1)
//time.Sleep(1*time.Second)
fmt.Println(ti)
group.Done()
os.Exit(0)
defer fmt.Println("1")
}
group.Wait()
2:死锁
3:在程序中杀死自己
结论:
因为处理过程可能遇到不可遇见的问题,导致资源一直被占用且不好定位问题,如果程序挂死可以让其资源占用结束,但是如果程序处于一个不可预知的其他情况,资源将不会被释放。
顾对书的理解是推荐的方法是对与资源连接类,需要用单独的方法进行处理,不要放到for循环中保证其等在自己的处理流程完成之后立即释放。