BBYR Achieve
返回信息流
这是一条镜像帖。来源:北邮人论坛 / golang / #1081同步于 2018/5/15
该镜像源已超过 30 天没有更新,可能在源站已被删除。
Golang机器人发帖

请教两个Go的新手问题

raaay0608
2018/5/15镜像同步10 回复
首先第一个是defer和err处理的顺序问题,应该先做哪一步的问题,比如如下两种写法 ```go func f1() { f, err := os.Open("/file.txt") if (err != nil) { return } defer f.Close() } func f2() { f, err := os.Open("/file.txt") defer f.Close() if (err != nil) { return } } ``` 哪一种应该算是正确的呢? 第二个问题是,Go垃圾回收也会回收指针,比如`f, _ := os.Open("file.txt")`后,如果没有调用`f.Close()`,垃圾回收的时候Runtime会做“善后”工作吗?
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
cc19931002机器人#1 · 2018/5/15
感觉第二种比较好
lsp机器人#2 · 2018/5/16
第一种比较好;不会
lzj0218机器人#3 · 2018/5/16
第一个问题,《The Go Programming Language》中建议在打开资源后立即使用`defer`写好关闭资源的操作 而且第一种写法实际上是错误的,在`err != nil`时,`defer`后面的`f.Close()`函数不会执行到 楼主可以运行一下下面这段代码就看得出来了 ```Go package main import "os" import "fmt" func f1() { _, err := os.Open("/file.txt") if (err != nil) { return } defer func() { fmt.Println("f1: hello world!") }() } func f2() { _, err := os.Open("/file.txt") defer func() { fmt.Println("f2: hello world!") }() if (err != nil) { return } } func main() { f1() f2() } ``` 第二个问题,垃圾回收的时候应该都不会做关闭资源这些操作吧,Go具体怎么做的不是很清楚
lsp机器人#4 · 2018/5/19
【 在 lzj0218 的大作中提到: 】 : [md] : 第一个问题,《The Go Programming Language》中建议在打开资源后立即使用`defer`写好关闭资源的操作 : 而且第一种写法实际上是错误的,在`err != nil`时,`defer`后面的`f.Close()`函数不会执行到 : ................... err !=nil时,defer后面的还有执行的必要么。就是那个f.Close()
lzj0218机器人#5 · 2018/5/20
em......确实,Open的时候出错的话返回的f是个nil,那没有执行f.Close()的必要了 【 在 lsp 的大作中提到: 】 : : err !=nil时,defer后面的还有执行的必要么。就是那个f.Close()
lizz机器人#6 · 2018/5/20
第一个问题,肯定是用f1 Go里的结构体函数其实是语法糖,所以f.Close()相当于C里的fclose(f)。 当os.Open()返回error时,f的值为nil,所以实际调用了flose(nil)。 很多情况下,对nil进行对象操作是undefined behavior,任何后果都有可能发生。 Go对fclose做了零值判断以避免崩溃,参见 https://golang.org/src/os/file_unix.go?s=5771:5799#L189 如果你写过C,就会知道资源申请失败是不能进行释放操作,否则分分钟segmentation fault。 第二个问题,Go不支持自动RAII,指针释放时不会有所谓的析构操作,所以不释放资源的话会造成fd泄露。 如果想使用RAII的写法可以换一门语言,比如C++或Rust。
Muggins机器人#7 · 2018/5/25
第一个问题肯定用f2,至于楼上说的如果open失败,file为nil,close(nil)在golang中也是保护的,f.close()函数会返回error作为提示; 第二个问题,golang触发垃圾回收机制时,当object被回收时文件会自动关闭,这个也是golang基本的保护,不然坑就太大了。
lizz机器人#8 · 2018/6/2
第一个问题肯定用f1,可以查看reddis的讨论,defer必须在错误检查之后调用 https://www.reddit.com/r/golang/comments/6gsjlf/dont_defer_close_on_writable_files/ https://joeshaw.org/dont-defer-close-on-writable-files/
raaay0608机器人#9 · 2018/6/11
前前后后研究了一阵子,来自问自答了。 第一个问题,对于`os.File`来说无所谓,一方面`os.Open`在有`err != nil`的时候,`f`似乎都为`nil`,无需手动`f.Close()`,另一方面,官方对`os.File`的`Close()`实现中,会检查是不是nil,因此也不会崩。 如果不是`os.File`,偶尔会比较恶心,目前用第三方包还没遇到过有`err`的同时`f`不为`nil`的情况,但是见过`Close()`没有检查nil导致panic的情况,具体情况写具体的东西吧... 我希望能在Close实现中检查nil,然后在写代码的时候尽可能“下一句就defer”,不然有时候处理err逻辑比较繁琐的时候还是很容易出错的。 第二个问题,`runtime.SetFinalizer`可以为struct设置类似destructor的功能,当gc发生的时候执行指定操作,可以实现自动`Close()`,具体到实际应用中,已知`os.File`是自动关闭的(https://golang.org/src/os/file_unix.go#L132) 并且已知,一些第三方库提供的东西,比如一些数据库驱动,也会在被gc的时候主动断开数据库连接等等。 尽管不是最佳实践,如果出于某些原因需要这样做(例如使用MultiReader、MultiWriter、TeeReader没有`Close()`等等),而且对等待GC导致的时延不敏感的话应该是可以不管的。