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 块就总会执行的机制吗??
1
xmge 2020-06-20 15:57:08 +08:00
感觉问题有点乱,整理一下:
1. defer f.Close 的 error 处理吗?如何处理? 2.是不是每个 goroutine 中都要写 defer {err := recover } 来捕捉异常? |
2
reus 2020-06-20 16:07:13 +08:00
|
3
lewinlan 2020-06-20 17:14:31 +08:00 via Android
打开的资源类型不同,err 的影响肯定也不同。自己看源码注释决定。
|
4
Aoang 2020-06-20 17:44:23 +08:00 via Android
这段时间用了一个三方库,库里面到处都是 panic 那种,它就是属于什么 error 都会处理的。
我用这个库的时候,没有任何感受…( 不写 recover 的话,一跑就 panic,没有一个完善的错误处理机制,不给调用者处理的机会。 |
5
sagaxu 2020-06-20 17:49:09 +08:00 via Android
把一个 exception 搞成这样也是大道至简么
|
6
virusdefender 2020-06-20 18:32:28 +08:00
一般 close 失败了也没啥补救的办法,打印日志意义也不大,我选择直接 defer 忽略
|
7
input2output 2020-06-20 18:44:07 +08:00
|
8
fengjianxinghun 2020-06-20 18:51:40 +08:00
大道至简还怕这种问题?
|
9
Mohanson 2020-06-20 18:55:05 +08:00 4
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 后资源一定是已经释放的状态即可.** |
10
reus 2020-06-20 19:06:51 +08:00 7
扯“大道至简”的傻逼,能不能不懂就闭嘴?
|
11
xmge 2020-06-20 19:28:03 +08:00 1
```
f, err := os.Create(fn) if err != nil { return err } defer f.Close() ``` 在官方库中也是这么写的,应该如楼上所言,这个 error 不用管 |
12
lcode 2020-06-20 21:08:49 +08:00
可以不处理,一般的 lint 工具也会有 ignore defer 类似的选项
如果非要处理,可以 1. 将错误返回,但是注意这里如果返回的话可能会覆盖代码之前的逻辑中的错误,所以最好组合到一起返回 2. 返回错误时打印日志,或直接将定义一个 log.CallFailed(func ())类似的函数,后期运维的时候可以看到哪里 close 时出错了 3. 排除掉应该返回错误的情况后,直接 panic |
13
hellodudu86 2020-06-20 23:05:14 +08:00
1.err 要处理的话最好就是返回给上层调用,有多层上报的话可以视情况 wrap 进去当前调用层的信息,上层判断的时候使用 errors.is()来处理,这样底层的代码都比较简洁且不会陷入面对错误编程的困境,可以参考下这篇文章 https://blog.golang.org/go1.13-errors
|
14
hellodudu86 2020-06-20 23:07:35 +08:00
2.开新的 goroutine 一般都会在 defer 里面 recover 来保证不崩溃
|
15
CRVV 2020-06-21 01:05:29 +08:00 via Android
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 了,这个问题也没多大影响 |
16
ujued 2020-06-21 08:24:32 +08:00 via iPhone
有不得不处理的时候,用匿名函数:
defer func(){ err := f.Close() // Handle or Throw }() |
17
dawniii 2020-06-21 15:44:57 +08:00
@input2output 这篇文章的观点好像是不对的。说是 close 的时候,可能有 buffer 没有落盘?可是 go 的 write 操作没有应用层 buffer 的,直接就是 write 系统调用写到 page cache 了,这时候就算进程挂了,内核也应该会自动落盘。
|