在 Go 中使用 defer:最佳实践和常见用例
来源:dev.to
时间:2024-12-02 18:00:46 110浏览 收藏
目前golang学习网上已经有很多关于Golang的文章了,自己在初次阅读这些文章中,也见识到了很多学习思路;那么本文《在 Go 中使用 defer:最佳实践和常见用例》,也希望能帮助到大家,如果阅读完后真的对你学习Golang有帮助,欢迎动动手指,评论留言并分享~
在 go 中,defer 关键字是一个强大的工具,可以帮助管理资源并确保函数退出时执行清理操作。延迟函数在周围函数返回时执行,无论它正常返回、由于错误还是由于恐慌。这可以确保无论函数如何退出,清理代码都会运行,使资源管理更简单、更可靠。
关于延期的要点:
- 执行时机:当周围函数返回时,延迟函数按照 lifo(后进先出)顺序执行,无论是完成执行、遇到 return 语句还是由于恐慌。
- 资源管理:它有助于自动关闭文件和网络连接等资源、解锁互斥体以及执行其他清理任务。
目录
- 1。多个defer语句的顺序
- 2。资源清理
- 3。解锁互斥体
- 4。释放数据库连接
- 5。恢复状态
- 6。处理恐慌
- 7。记录和计时
- 8。刷新缓冲区
- 9。处理 http 请求体
- 10。不延迟关闭失败的开口的风险
- 11。在检查文件是否打开之前使用 defer 的风险
- 12。结论
1. 多个defer语句的顺序
在go中,函数内的多个defer语句按照reverse出现的顺序执行。这对于管理多个清理任务非常有用,确保它们在函数退出时按特定顺序执行。
func examplefunction() { fmt.println("start of function") defer fmt.println("first defer: executed last") defer fmt.println("second defer: executed second") defer fmt.println("third defer: executed first") fmt.println("end of function") }
输出:
start of function end of function third defer: executed first second defer: executed second first defer: executed last
2. 资源清理
defer 最常见的用途之一是确保文件等资源在不再需要后正确关闭。
func processfile(filename string) error { file, err := os.open(filename) if err != nil { return err // return the error if opening the file fails } defer file.close() // ensure the file is closed when the function exits // process the file... return nil }
os.file 实现了 io.readcloser,所以这里使用 defer 可以确保文件正确关闭,防止资源泄漏。
3. 解锁互斥体
处理并发时,释放锁以防止死锁至关重要。 defer 有助于有效管理互斥体。
var mu sync.mutex func criticalsection() { mu.lock() defer mu.unlock() // ensure the mutex is unlocked when the function exits // critical section... }
通过推迟 mu.unlock(),可以确保互斥锁始终被释放,从而使代码更易于理解且不易出错。
4. 释放数据库连接
不再需要数据库连接时应关闭以释放资源
func querydatabase() error { db, err := sql.open("driver", "database=example") if err != nil { return err } defer db.close() // ensure the database connection is closed when the function exits // query the database... return nil }
5. 恢复状态
- 示例:更改和恢复工作目录
更改工作目录时,将其恢复到原始状态很重要
func changedirectory() error { olddir, err := os.getwd() if err != nil { return err } err = os.chdir("/tmp") if err != nil { return err } defer os.chdir(olddir) // restore the working directory when the function exits // work in /tmp... return nil }
使用defer可以轻松自动恢复原目录
6. 处理恐慌
- 示例:从恐慌中恢复
defer 可用于从恐慌中恢复并优雅地处理错误。
func safefunction() { defer func() { if r := recover(); r != nil { log.println("recovered from panic:", r) } }() // code that might panic... }
通过推迟处理恐慌的函数,您可以确保您的应用程序即使在遇到意外错误时也保持稳健。
7. 记录和计时
- 示例:定时函数执行
defer 对于测量执行时间或在函数退出时进行记录非常有用。
func measuretime() { start := time.now() defer func() { duration := time.since(start) log.printf("execution time: %v", duration) }() // code to measure... }
这种方法简化了计时代码并确保在函数完成时记录持续时间。
8. 冲洗缓冲器
- 示例:刷新缓冲 i/o
缓冲的i/o操作应该被刷新以确保所有数据都被写出。
func bufferedwrite() { buf := bufio.newwriter(os.stdout) defer buf.flush() // ensure the buffer is flushed when the function exits buf.writestring("hello, world!") }
这里使用 defer 可以保证所有缓冲的数据在函数完成之前被写出。
9. 处理 http 请求体
- 示例:关闭http请求体
http请求体实现了io.readcloser,因此使用后关闭它们以释放资源并避免泄漏至关重要。
func handlerequest(req *http.request) error { // ensure that the request body is closed when the function exits defer func() { if err := req.body.close(); err != nil { log.println("error closing request body:", err) } }() body, err := io.readall(req.body) if err != nil { return err } // process the body... fmt.println("request body:", string(body)) return nil }
通过推迟 req.body.close(),您可以确保主体正确关闭,即使在读取或处理主体时发生错误也是如此。
10. 不延期关闭失败的开仓的风险
当您在 go 中打开文件或其他资源时,确保不再需要该资源时正确关闭该资源至关重要。但是,如果您在错误检查后尝试关闭资源而不使用 defer,则可能会给您的代码带来风险。
- 没有延迟的示例
file, err := os.open(filename) if err != nil { return err // handle error } // risk: if something goes wrong before this point, the file might never be closed // additional operations here... file.close() // attempt to close the file later
在 go 中不使用 defer 关闭资源可能会导致意想不到的后果,例如尝试关闭从未成功打开的资源,从而导致意外行为或恐慌。此外,如果在显式 close() 调用之前发生错误,资源可能会保持打开状态,从而导致泄漏并耗尽系统资源。随着代码变得越来越复杂,确保所有资源正确关闭变得越来越困难,从而增加了忽略关闭操作的可能性。
11. 在检查文件是否打开之前使用 defer 的风险
在 go 中,在验证资源(如文件)已成功打开后放置 defer 语句至关重要。
在错误检查之前放置延迟可能会带来一些风险和不良行为。
错误使用示例
file, err := os.Open(fileName) defer file.Close() // Incorrect: This should be deferred after the error check if err != nil { return err // Handle error } // Additional operations here...
在检查 os.open 是否成功之前放置 defer file.close() 可能会导致几个问题。如果文件未打开并且为 nil,尝试关闭它会导致运行时恐慌,因为即使发生错误,go 也会执行所有延迟函数。这种方法还会使代码产生误导,暗示文件已成功打开,而实际上它可能没有打开,这使理解和维护变得复杂。此外,如果确实发生恐慌,调试就会变得更具挑战性,尤其是在复杂的代码库中,因为将问题追溯到错误的延迟可能需要额外的努力。
12. 结论
go 中的 defer 关键字通过确保函数退出时自动执行清理操作来简化资源管理并增强代码清晰度。通过在这些常见场景中使用 defer,您可以编写更健壮、可维护且无错误的代码。
今天关于《在 Go 中使用 defer:最佳实践和常见用例》的内容介绍就到此结束,如果有什么疑问或者建议,可以在golang学习网公众号下多多回复交流;文中若有不正之处,也希望回复留言以告知!
-
505 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
420 收藏
-
410 收藏
-
249 收藏
-
389 收藏
-
101 收藏
-
284 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习