V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
SimbaPeng
V2EX  ›  Go 编程语言

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

  •  
  •   SimbaPeng · 2020-06-20 15:17:23 +08:00 · 4050 次点击
    这是一个创建于 1621 天前的主题,其中的信息可能已经有所发展或是发生改变。

    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 条附言  ·  2020-06-20 16:39:40 +08:00
    确实感觉问题有点乱,总结为 2 点:

    1. defer f.Close 的 error 处理吗?如何处理?

    2. 想找一个不管遇到什么情况,每个 goroutine 里的 defer 都能保证执行的方法,类似 try...finally 。
    17 条回复    2020-06-21 15:44:57 +08:00
    xmge
        1
    xmge  
       2020-06-20 15:57:08 +08:00
    感觉问题有点乱,整理一下:

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

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

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

    不写 recover 的话,一跑就 panic,没有一个完善的错误处理机制,不给调用者处理的机会。
    sagaxu
        5
    sagaxu  
       2020-06-20 17:49:09 +08:00 via Android
    把一个 exception 搞成这样也是大道至简么
    virusdefender
        6
    virusdefender  
       2020-06-20 18:32:28 +08:00
    一般 close 失败了也没啥补救的办法,打印日志意义也不大,我选择直接 defer 忽略
    fengjianxinghun
        8
    fengjianxinghun  
       2020-06-20 18:51:40 +08:00
    大道至简还怕这种问题?
    Mohanson
        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 后资源一定是已经释放的状态即可.**
    reus
        10
    reus  
       2020-06-20 19:06:51 +08:00   ❤️ 7
    扯“大道至简”的傻逼,能不能不懂就闭嘴?
    xmge
        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 不用管
    lcode
        12
    lcode  
       2020-06-20 21:08:49 +08:00
    可以不处理,一般的 lint 工具也会有 ignore defer 类似的选项
    如果非要处理,可以
    1. 将错误返回,但是注意这里如果返回的话可能会覆盖代码之前的逻辑中的错误,所以最好组合到一起返回
    2. 返回错误时打印日志,或直接将定义一个 log.CallFailed(func ())类似的函数,后期运维的时候可以看到哪里 close 时出错了
    3. 排除掉应该返回错误的情况后,直接 panic
    hellodudu86
        13
    hellodudu86  
       2020-06-20 23:05:14 +08:00
    1.err 要处理的话最好就是返回给上层调用,有多层上报的话可以视情况 wrap 进去当前调用层的信息,上层判断的时候使用 errors.is()来处理,这样底层的代码都比较简洁且不会陷入面对错误编程的困境,可以参考下这篇文章 https://blog.golang.org/go1.13-errors
    hellodudu86
        14
    hellodudu86  
       2020-06-20 23:07:35 +08:00
    2.开新的 goroutine 一般都会在 defer 里面 recover 来保证不崩溃
    CRVV
        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 了,这个问题也没多大影响
    ujued
        16
    ujued  
       2020-06-21 08:24:32 +08:00 via iPhone
    有不得不处理的时候,用匿名函数:
    defer func(){
    err := f.Close()
    // Handle or Throw
    }()
    dawniii
        17
    dawniii  
       2020-06-21 15:44:57 +08:00
    @input2output 这篇文章的观点好像是不对的。说是 close 的时候,可能有 buffer 没有落盘?可是 go 的 write 操作没有应用层 buffer 的,直接就是 write 系统调用写到 page cache 了,这时候就算进程挂了,内核也应该会自动落盘。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1050 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 19:53 · PVG 03:53 · LAX 11:53 · JFK 14:53
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.