思考
开始之前,先考虑下下面的代码的执行结果:
代码语言:javascript复制package main
import "fmt"
func test() int {
i := 0
defer func() {
fmt.Println("defer1")
}()
defer func() {
i = 1
fmt.Println("defer2")
}()
return i
}
func main() {
fmt.Println("return", test())
}
defer介绍
defer
是 Go 编程语言中的一个关键字,用于在函数执行结束后延迟执行指定的函数调用。defer
的使用非常灵活,它通常用于执行一些清理操作、资源释放、日志记录等任务。以下是对 defer
的详细介绍:
1.defer
的语法:•defer
后面跟随一个函数调用,该函数会在包含 defer
语句的函数执行完毕后被调用。•语法示例:defer someFunction()
2.执行时机:•defer
函数调用会在包含 defer
语句的函数返回之前执行,即使在函数中间有 return
语句也是如此。•这确保了 defer
中的操作在函数结束时始终执行,无论函数是正常返回还是出现异常。3.多个 defer
语句:•一个函数可以包含多个 defer
语句,它们会以后进先出(LIFO)的顺序执行。•这意味着最后一个出现的 defer
语句会最先执行,而最先出现的 defer
语句会最后执行。4.常见用途:•资源释放:defer
常用于关闭文件、释放锁、释放内存等资源管理任务,确保资源在函数结束时得到正确释放。•错误处理:defer
可以用于记录错误日志或执行清理操作,以确保即使发生错误,资源也能得到释放。•跟踪代码执行:defer
还可以用于记录函数的执行情况,以进行性能分析或跟踪代码路径。5.示例: 下面是一个使用 defer
的示例,演示了文件的打开和关闭操作:
6.注意事项:•defer
不仅用于函数的返回,还可以用于方法(类似于面向对象编程中的析构函数)。•defer
中的参数会在 defer
语句执行时被求值,因此如果你有多个 defer
语句使用相同的参数,它们会被依次求值。•在某些情况下,要特别小心 defer
中的闭包,以避免出现意外的行为。
defer执行时机
defer
语句中的函数调用会在包含 defer
语句的函数返回之前执行。无论函数是正常返回还是在执行中发生了 panic,defer
中的函数都会按照后进先出(LIFO)的顺序执行。这确保了在函数结束时进行清理和释放资源,以及在函数执行期间处理错误或日志记录等任务。
以下是关于 defer
执行时机的详细解释:
1.正常返回时的 defer
执行:•在函数执行过程中,当遇到 defer
语句时,不会立即执行 defer
中的函数调用,而是将它们压入一个栈中,以便在函数返回时执行。•当函数执行完毕并准备返回时,栈中的 defer
函数调用会按照后进先出的顺序执行,确保最后一个 defer
最先执行。2.发生 panic 时的 defer
执行:•如果函数在执行中发生 panic(异常),同样会执行 defer
中的函数,然后再传播 panic,这允许在 panic 后执行清理操作。•这可以用来释放资源、记录错误信息、关闭连接等。
下面是一个示例,说明了 defer
的执行时机:
func exampleFunction() {
defer fmt.Println("Deferred 1")
defer fmt.Println("Deferred 2")
fmt.Println("Function body")
panic("Something went wrong")
}
func main() {
exampleFunction()
}
在这个示例中,exampleFunction
包含两个 defer
语句和一个 panic。当 exampleFunction
调用时,它首先打印 "Function body",然后执行 defer
中的函数。在 panic 发生后,defer
语句中的函数会按照后进先出的顺序执行。所以,main
函数的输出将是:
Function body
Deferred 2
Deferred 1
panic: Something went wrong
正如示例所示,defer
中的函数在函数返回之前或在 panic 发生后都会执行,这使得它在资源管理和错误处理方面非常有用。
结束
现在回到最开始的问题,在上面的代码中,test
函数包含两个 defer
语句,以及一个 return
语句。在 main
函数中,我们调用 test
并输出其返回值。让我们来解释每一步并分析输出的结果:
1.i
初始化为 0
。2.第一个 defer
语句中的匿名函数只是打印 "defer1",不对 i
进行任何修改。3.第二个 defer
语句中的匿名函数增加了 i
的值,然后打印 "defer2"。
现在,让我们分析 test
函数的执行流程:
1.i
初始化为 0
。2.第一个 defer
语句注册的函数(打印 "defer1")会在函数返回之前执行,但它没有影响 i
的值。3.接下来,第二个 defer
语句注册的函数(增加 i
的值并打印 "defer2")也会在函数返回之前执行,但在执行时,i
的值仍然为 0
。4.return i
语句返回 0
。
因此,test
函数返回 0
,但在执行过程中,两个 defer
函数都被执行,按照注册的顺序分别打印 "defer1" 和 "defer2"。
在 main
函数中,我们调用 test
并输出其返回值,因此最终的输出是:
defer2
defer1
return 0
这是因为 defer2
和 defer1
的输出分别在 test
函数调用结束之前执行,而 return 0
的结果在函数返回后被 main
函数输出。
声明:本作品采用署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)[1]进行许可,使用时请注明出处。 Author: mengbin[2] blog: mengbin[3] Github: mengbin92[4] cnblogs: 恋水无意[5]
References
[1]
署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0): https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh
[2]
mengbin: mengbin1992@outlook.com
[3]
mengbin: https://mengbin.top
[4]
mengbin92: https://mengbin92.github.io/
[5]
恋水无意: https://www.cnblogs.com/lianshuiwuyi/