教程集 www.jiaochengji.com
教程集 >  Golang编程  >  golang教程  >  正文 Golang defer的运行时机和遇到的坑

Golang defer的运行时机和遇到的坑

发布时间:2022-12-24   编辑:jiaochengji.com
教程集为您提供Golang defer的运行时机和遇到的坑等资源,欢迎您收藏本站,我们将为您提供最新的Golang defer的运行时机和遇到的坑资源

一、defer概述

defergolang 中独有的流程控制语句,用于延迟指定语句的运行时机,只能运行于函数的内部,且当他所属函数运行完之后它才会被调用。例如:

1
2
3
4
func deferTest(){
defer fmt.Println("HelloDefer")
fmt.Println("HelloWorld")
}

它会先打印出HelloWorld ,然后再打印出HelloDefer

一个函数中如果有多个defer ,运行顺序和函数中的调用顺序相反,因为它们都是被写在了栈中:

1
2
3
4
5
func deferTest(){
defer fmt.Println("HelloDefer1")
defer fmt.Println("HelloDefer2")
fmt.Println("HelloWorld")
}

运行结果:

1
2
3
fmt.Println("HelloDefer2")
fmt.Println("HelloDefer1")
fmt.Println("HelloWorld")

二、defer和return

在包含有return 语句的函数中,defer 的运行顺序位于return 之后,但是defer 所运行的代码片段会生效:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func main(){
fmt.Println(deferReturn)
}
func deferReturn() int{
i := 1
defer func(){
fmt.Println("Defer")
i = 1
}()
return func()int{
fmt.Println("Return")
return i
}()
}

运行结果:

1
2
3
Return
Defer
1

这里很明显就能看到defer 是在return 之后运行的!但是有一个问题是defer 里执行了语句i =1 ,按照这个逻辑的话返回的i 值应该是2 而不是1 。这个问题是由于return 的运行机制导致的:return 在返回一个对象时,如果返回类型不是指针或者引用类型,那么return 返回的就不会是这个对象本身,而是这个对象的副本。

我们可以验证这一个观点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func main(){
...
fmt.Println("main: ", x, &x)
}
func deferReturn() int{
...
defer ...{
fmt.Println("Defer: ", i, &i)
...
}()
return ...{
fmt.Println("Return: ", i, &i)
...
}()
}

程序的输出为:

1
2
3
Return:     1 0xc042008238
Defer: 1 0xc042008238
main: 1 0xc042008230 //main函数中的i的地址和deferReturn()中的i的地址是不一样的

如果把函数的返回值改成指针类型,这时候的main函数中的返回值就会和函数体内的一致:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func main(){
x := deferReturn()
fmt.Println("main: ", x, *x)
}
func deferReturn()*int{
i := 1
p := &i
defer func() {
*p = 1
fmt.Println("defer: ", p, *p)
}()
return func() *int{
fmt.Println("Return: ", p, *p)
return p
}()
}

结果:

1
2
3
Return:     0xc0420361d0 1
defer: 0xc0420361d0 2
main: 0xc0420361d0 2

三、defer和panic

panic 会在defer 运行完之后才把恐慌扩散到其他函数:

1
2
3
4
func deferPanic(){
defer fmt.Println("HelloDefer")
panic("Hey, I"m panic")
}

结果:

1
2
3
4
5
6
7
HelloDefer  //会先输出defer部分的代码
panic: Hey, I"m panic
goroutine 1 [running]:
main.deferTest()
E:/code/golang/src/test_src/defer/main.go:12 0xfc
main.main()
E:/code/golang/src/test_src/defer/main.go:6 0x27

四、defer和for循环

不要在defer 内使用外部变量,可能会造成一些意想不到的错误:

1
2
3
4
5
6
7
func deferTest(){
for i := 0; i < 5; i {
defer func(){
fmt.Printf("%d", i)
}()
}
}

它的输出的结果是55555 ,原理很简单,因为defer会在for循环运行完后才会调用,for循环运行完时i的值为5,所以打印的i值会是55555

但是如果循环内的延时函数有参数传入,参数就会在当前defer 语句执行的时候求出:

1
2
3
4
5
6
7
func deferTest(){
for i := 0; i < 5; i {
defer func(i int){
fmt.Printf("%d", i)
}(i)
}
}

此时就会打印出43210 而不是55555 了。


到此这篇关于“Golang defer的运行时机和遇到的坑”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!

您可能感兴趣的文章:
Golang中defer关键字实现原理
Golang defer 使用时的坑
Go 中 defer 的 5 个坑 - 第一部分
go那些事儿|defer必掌握知识
defer函数参数求值简要分析
defer ,panic,recover
Golang基础--常见坑
Golang defer解读
16.defer让代码更清晰
recover 没有捕获异常_GO语言异常处理机制panic和recover分析

[关闭]
~ ~