Go 语言中 defer 文件关闭的正确姿势

文摘   2025-01-29 11:35   四川  

初学 Go 语言,常遇到的一个问题是如何妥善处理资源,比如文件的打开与关闭。尤其在程序运行出错时,忘记关闭文件会导致资源泄漏。今天说一下如何利用 defer 解决这个问题。不想搞复杂,就想通过简单的例子,让你一下明白这个常用的技巧。

举个例子,你要打开一个文件,读点内容,然后关闭它。如果正常步骤,你可以这么写:

go


file, err := os.Open("test.txt")
if err !=nil{
    // 处理错误
    return
}
// 读取文件内容
data :=make([]byte,100)
file.Read(data)

// 别忘了关闭文件
file.Close()

这里一切都好,但如果中间发生错误,忘记关闭文件,容易资源泄漏。每一次打开文件后都要想着去关,也让代码显得繁琐杂乱。这就该 defer 上场了。

来看看加了 defer 的写法:

go


file, err := os.Open("test.txt")
if err !=nil{
    // 处理错误
    return
}
// 用 defer 推迟关闭文件
defer file.Close()

// 读取文件内容
data :=make([]byte,100)
file.Read(data)

// 剩下的代码...
// 文件会在函数返回时自动关闭,无需再显式调用 Close

在这种写法中,一旦打开了文件,立刻就使用 defer 延迟关闭。这样的话,不管之后你代码出现什么问题,甚至是抛出了错误,Go 都会在函数返回前自动执行 file.Close()。这样一来,写代码的人不用再操心关文件的事,也能有效避免出错时资源泄漏。

有些朋友可能纳闷了,defer 放到那么前面,它凭什么能确保文件会恰好在最后关?这是个好问题。其实,defer 的行为类似堆栈,它会把要延迟执行的语句按顺序压到一个堆栈里,函数结束了,这些语句就按“后进先出”的顺序一个个跑。不像某些语言那种 try-finally 结构复杂,Go 的设计理念是简单直接地处理这种场景。

继续深入一点。你会发现,有时候涉及到多个资源要关闭,比如文件、连接等等,defer 依旧很好用。注意一下顺序即可:

go


file, err := os.Open("test.txt")
if err !=nil{
    return
}
defer file.Close()

conn, err := net.Dial("tcp","example.com:80")
if err !=nil{
    file.Close()// 别忘了先关闭已打开的文件
    return
}
defer conn.Close()

// 操作文件和连接...

如你所见,打开文件和连接的两个 defer,Go 自然帮你照顾它们的关闭顺序,代码干净了不少,原来要自行判断关闭的代码现在大大简化。能明显感觉到代码逻辑不再分散注意力。这时你可能感慨一句:“这样写的确省心了!”

得提一句,defer 会在返回之前执行,影响的是实际返回那个点,不是defer 一经书写后立刻执行。这么设计的好处在于回归直观——什么时候函数返回,什么时候善后处理,这都是 Go 细节方面用心做简化处理的结果。

你看,看上去是小小的一个关键字,巧妙用在文件操作里会让代码整整齐齐,心头的烦恼少了很多。再也不怕写着写着哪天代码炸了,无辜的小文件仍在磁盘深处默默占着资源流泪。这点应用的效果却是满满的省心感。这不,写代码逐渐少受烦恼纠葛。学一招灵活运用,Go 的快乐实际体会才觉明白。


粒粒快点跑
我是粒姐,11年老猎头,职业咨询顾问,曾创立两家猎头公司。 分享求职技巧和职场经验,职业愿景是帮助1000人找到心仪工作。 猎聘签约求职教练,1V1咨询,求职辅导,职业规划咨询,职场辅导。视频号:#粒粒快点跑
 最新文章