Go语言的defer关键字用于延迟调用,下面是关于Go语言defer关键字的一些基础概念:
1. defer关键字用于注册延迟调用;
2. 这些调用直到包含当前该defer关键字的函数执行完了才会被执行;
3. 如果定义了多个defer语句,按照先进后出的方式执行,也即后定义的defer语句会先被执行,这也比较好理解,如果之前的资源释放掉了,后面需要使用掉之前资源的语句就没法执行了(可以用来逆序输出内容);
4. defer语句中的变量,在defer声明的时候就决定了,也即在defer语句中使用到的变量的值为在defer语句之前的变量值(不包括匿名函)
defer语句主要有三个方面的用途:① 关闭文件句柄;② 锁资源释放;③ 数据库连接释放;通过defer机制,不论函数逻辑多复杂,都能保证在任何执行路径下,资源被释放,主要用于资源的管理;
多个defer关键字的执行顺序,下面的例子输出的数字为:3 2 1:
package main
import (
"fmt"
)
func f1() {
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
}
func main() {
f1()
}
延迟调用参数在注册时求值或复制,可用指针或闭包 "延迟" 读取:
package main
import (
"fmt"
)
func f1() {
x, y := 0, 0
x += 1
// 闭包环境
defer func(i int) {
fmt.Print(i, " ", x, " ")
}(x)
defer func() {
fmt.Print(y, " ")
}()
defer func() {
fmt.Print(x+y, " ")
}()
x += 2
y += 2
}
func main() {
f1()
fmt.Println()
f1()
}
输出结果:
5 2 1 3
5 2 1 3
defer遇到闭包:
package main
import "fmt"
func f1() {
a := []int{1, 4, 6, 2, 7}
for _, v := range a {
defer func() {
fmt.Println(v)
}()
}
}
func main() {
f1()
}
输出结果:由于闭包使用到的变量v在执行的时候已经变成7了,所以所有的输出结果都为7:
7
7
7
7
7
package main
import "fmt"
func f(x int) int {
fmt.Printf("传递的参数地址: %p\n", &x)
defer fmt.Println("1: ", x)
defer func() {
x += 1
fmt.Printf("defer函数中参数地址: %p\n", &x)
}()
fmt.Println("2: ", x)
return x
}
func main() {
fmt.Println("3: ", f(10))
}
输出结果:
传递的参数地址: 0xc000018098
2: 10
defer函数中参数地址: 0xc000018098
1: 10
3: 10
容易发生的错误:当方法接受者为指针类型的函数的时候,如果调用这种类型的函数那么使用循环输出值的时候为最后一个元素的值:
package main
import "fmt"
type Stu struct {
name string
}
func (stu *Stu) Test() {
fmt.Println(stu.name)
}
func main() {
stus := []Stu{{"a"}, {"b"}, {"c"}}
for _, v := range stus {
defer v.Test()
}
}
输出结果:
c
c
c
我们可以修改一下上面例子增加一个接受者为值类型的Test2函数,在Test2函数中调用Test函数,那么就可以得到我们预期的结果:
package main
import "fmt"
type Stu struct {
name string
}
func (stu *Stu) Test() {
fmt.Println(stu.name)
}
func (stu Stu) Test2() {
stu.Test()
}
func main() {
stus := []Stu{{"a"}, {"b"}, {"c"}}
for _, v := range stus {
defer v.Test2()
}
}
或者使用下面的方式,也可以输出预期的结果:
package main
import "fmt"
type Stu struct {
name string
}
func (stu *Stu) Test() {
fmt.Println(stu.name)
}
func main() {
stus := []Stu{{"a"}, {"b"}, {"c"}}
for _, v := range stus {
v1 := v
defer v1.Test()
}
}
从上面的例子中可以看到defer语句在执行的时候,函数调用的参数会被保存起来,但是不执行,复制了一份,并且从这个例子中可以发现go语言并没有把结构体指针类型的变量看成是参数。
当存在多个defer语句注册的时候,按照先进后出的方式执行,如果某个函数或者延迟调用发生错误,这些调用依旧会被执行:
package main
import "fmt"
func f(a, b int) {
defer func() {
fmt.Println(a / b)
}()
defer fmt.Println(a)
defer fmt.Println(b)
}
func main() {
f(1, 0)
}
defer与return语句的使用:在具有具体名字返回值的函数中(也即返回值有具体的变量名字,下面这个例子为变量i),defer语句可以修改函数中命名返回值:
package main
import "fmt"
func f(a int) (i int) {
defer func() {
i += 20
}()
return a + 10
}
func main() {
fmt.Println(f(0))
}
输出结果为:30
package main
import "fmt"
func f1() {
for i := 0; i < 5; i++ {
defer fmt.Println(i)
}
}
func f2() {
for i := 0; i < 5; i++ {
defer func() {
fmt.Println(i)
}()
}
}
func f3() {
for i := 0; i < 5; i++ {
defer func(n int) {
fmt.Println(n)
}(i)
}
}
func f4() int {
t := 5
defer func() {
t++
}()
return t
}
func f5() (r int) {
defer func() {
r++
}()
return 0
}
func f6() (r int) {
t := 5
defer func() {
t = t + 5
}()
return t
}
func f7() (r int) {
fmt.Printf("%p\n", &r)
defer func(r int) {
fmt.Printf("%p\n", &r)
r = r + 5
}(r)
return 1
}
func main() {
f7()
}
f1输出结果:
4
3
2
1
0
f2输出结果:
5
5
5
5
5
f3输出结果:
4
3
2
1
0
调用f4输出结果:5
调用f5输出结果:1
调用f6输出结果:5
调用f7输出结果:
0xc000018098
0xc0000180d0
1