大家写 golang 的时候是否处理 f.Close 返回的错误值?

2020-06-20 15:17:23 +08:00
 SimbaPeng

golang 里打开某个资源,通常都会在下面紧接着 defer f.Close, 这个 f 是指任何需要 close 的资源,没有特指文件。

但是 close 方法都会返回一个 err,大家在实际编码中会去处理这个 err 吗,如果处理,怎么处理呢?

记录日志之后 os.Exit() or panic ?

这 2 个我感觉都不好用啊,os.Exit()会跳过 defer 直接终止程序,也就是说如果程序中还有其他资源在 defer 里清理,都会失效。

而 panic 只会执行当前 goroutine 的 defer, 然后程序崩溃退出,如果程序在多个 goroutine 中都有 defer 要执行,那么其他 goroutine 的 defer 都不能保证执行了。难道我要在每个 goroutine 都捕获 panic 来保证程序不崩溃吗??

golang 里就没有 finally 这种只要进了 try 块就总会执行的机制吗??

4005 次点击
所在节点    Go 编程语言
17 条回复
xmge
2020-06-20 15:57:08 +08:00
感觉问题有点乱,整理一下:

1. defer f.Close 的 error 处理吗?如何处理?
2.是不是每个 goroutine 中都要写 defer {err := recover } 来捕捉异常?
reus
2020-06-20 16:07:13 +08:00
一般不处理,如果非要处理的话,这样写

https://gist.github.com/reusee/25836c8a6faac925fe52c1c73d122a04
lewinlan
2020-06-20 17:14:31 +08:00
打开的资源类型不同,err 的影响肯定也不同。自己看源码注释决定。
Aoang
2020-06-20 17:44:23 +08:00
这段时间用了一个三方库,库里面到处都是 panic 那种,它就是属于什么 error 都会处理的。

我用这个库的时候,没有任何感受…(

不写 recover 的话,一跑就 panic,没有一个完善的错误处理机制,不给调用者处理的机会。
sagaxu
2020-06-20 17:49:09 +08:00
把一个 exception 搞成这样也是大道至简么
virusdefender
2020-06-20 18:32:28 +08:00
一般 close 失败了也没啥补救的办法,打印日志意义也不大,我选择直接 defer 忽略
input2output
2020-06-20 18:44:07 +08:00
fengjianxinghun
2020-06-20 18:51:40 +08:00
大道至简还怕这种问题?
Mohanson
2020-06-20 18:55:05 +08:00
close 是一个系统调用, 它出错的情况只有:

```
ERRORS
The close() system call will fail if:
[EBADF] fildes is not a valid, active file descriptor.
[EINTR] Its execution was interrupted by a signal.
[EIO] A previously-uncommitted write(2) encountered an input/output
error.
```

程序中经常遇到的是 EIO, 也就是在在 close 之前, read/write 已经报错了(比如文件 fd 已经提前关闭, 网络的话 TCP 连接已经断开等), 这种情况忽略 close 的 err 才是正确的逻辑. **因为调用 close 的核心目的是释放资源, 而不管在这次调用之前资源是否已经被释放了, 程序只保证在该次调用 close 后资源一定是已经释放的状态即可.**
reus
2020-06-20 19:06:51 +08:00
扯“大道至简”的傻逼,能不能不懂就闭嘴?
xmge
2020-06-20 19:28:03 +08:00
```
f, err := os.Create(fn)
if err != nil {
return err
}
defer f.Close()
```

在官方库中也是这么写的,应该如楼上所言,这个 error 不用管
lcode
2020-06-20 21:08:49 +08:00
可以不处理,一般的 lint 工具也会有 ignore defer 类似的选项
如果非要处理,可以
1. 将错误返回,但是注意这里如果返回的话可能会覆盖代码之前的逻辑中的错误,所以最好组合到一起返回
2. 返回错误时打印日志,或直接将定义一个 log.CallFailed(func ())类似的函数,后期运维的时候可以看到哪里 close 时出错了
3. 排除掉应该返回错误的情况后,直接 panic
hellodudu86
2020-06-20 23:05:14 +08:00
1.err 要处理的话最好就是返回给上层调用,有多层上报的话可以视情况 wrap 进去当前调用层的信息,上层判断的时候使用 errors.is()来处理,这样底层的代码都比较简洁且不会陷入面对错误编程的困境,可以参考下这篇文章 https://blog.golang.org/go1.13-errors
hellodudu86
2020-06-20 23:07:35 +08:00
2.开新的 goroutine 一般都会在 defer 里面 recover 来保证不崩溃
CRVV
2020-06-21 01:05:29 +08:00
Java 的 finally 也不保证能执行到
https://stackoverflow.com/questions/1410951/how-does-javas-system-exit-work-with-try-catch-finally-blocks

但是 Python 在这个情况下会执行 finally

defer 和其它语言里的 finally 基本上是等价的,直接用就是了

我觉得 panic 和 os.Exit 在正常运行的程序里不多见,出现了就需要改 bug 了,这个问题也没多大影响
ujued
2020-06-21 08:24:32 +08:00
有不得不处理的时候,用匿名函数:
defer func(){
err := f.Close()
// Handle or Throw
}()
dawniii
2020-06-21 15:44:57 +08:00
@input2output 这篇文章的观点好像是不对的。说是 close 的时候,可能有 buffer 没有落盘?可是 go 的 write 操作没有应用层 buffer 的,直接就是 write 系统调用写到 page cache 了,这时候就算进程挂了,内核也应该会自动落盘。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/683301

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX