golang----defer延时函数的理解

//通过学习我们了解了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循环中保证其等在自己的处理流程完成之后立即释放。

猜你喜欢

转载自blog.csdn.net/weixin_40669549/article/details/88935102