关于Go defer的详细使用

软件发布|下载排行|最新软件

当前位置:首页IT学院IT技术

关于Go defer的详细使用

周伯通的麦田   2019-12-04 我要评论

先抛砖引玉defer的延迟调用:
defer特性:

1. 关键字 defer 用于注册延迟调用。
2. 这些调用直到 return 前才被执。因此,可以用来做资源清理。
3. 多个defer语句,按先进后出的方式执行。
4. defer语句中的变量,在defer声明时就决定了。

defer用途:

1. 关闭文件句柄
2. 锁资源释放
3. 数据库连接释放

好,废话不多说,实例加深理解,我们先看看一段代码

package main

import "fmt"

func main() {
    var users [5]struct{}
    for i := range users {
        defer fmt.Println(i)
    }
}

输出:4 3 2 1 0 ,defer 是先进后出,这个输出没啥好说的。

我们把上面的代码改下:
defer 换上闭包:

package main

import "fmt"

func main() {
    var users [5]struct{}
    for i := range users {
        defer func() { fmt.Println(i) }()
    }
}

输出:4 4 4 4 4,很多人也包括我。预期的结果不是 4 3 2 1 0 吗?官网对defer 闭包的使用大致是这个意思:

函数正常执行,由于闭包用到的变量 i 在执行的时候已经变成4,所以输出全都是4。那么 如何正常输出逾期的 4 3 2 1 0 呢?
不用闭包,换成函数:

package main

import "fmt"

func main() {
    var users [5]struct{}
    for i := range users {
        defer Print(i)
    }
}
func Print(i int) {
    fmt.Println(i)
}

函数正常延迟输出:4 3 2 1 0。

我们再举一个可能一不小心会犯错的例子:
defer调用引用结构体函数

package main

import "fmt"

type Users struct {
    name string
}

func (t *Users) GetName() { // 注意这里是 * 传地址 引用Users
    fmt.Println(t.name)
}
func main() {
    list := []Users{{"乔峰"}, {"慕容复"}, {"清风扬"}}
    for _, t := range list {
        defer t.GetName()
    }
}

输出:清风扬 清风扬 清风扬。

这个输出并不会像我们预计的输出:清风扬 慕容复 乔峰

可是按照前面的go defer函数中的使用说明,应该输出清风扬 慕容复 乔峰才对啊?

那我们换一种方式来调用一下

package main

import "fmt"

type Users struct {
    name string
}

func (t *Users) GetName() { // 注意这里是 * 传地址 引用Users
    fmt.Println(t.name)
}
func GetName(t Users) { // 定义一个函数,名称自定义
    t.GetName() // 调用结构体USers的方法GetName
}
func main() {
    list := []Users{{"乔峰"}, {"慕容复"}, {"清风扬"}}
    for _, t := range list {
        defer GetName(t)
    }
}

输出:清风扬 慕容复 乔峰。

这个时候输出的就是所谓"预期"滴了

当然,如果你不想多写一个函数,也很简单,可以像下面这样(改2处),同样会输出清风扬 慕容复 乔峰

package main

import "fmt"

type Users struct {
    name string
}

func (t *Users) GetName() { // 注意这里是 * 传地址 引用Users
    fmt.Println(t.name)
}
func GetName(t Users) { // 定义一个函数,名称自定义
    t.GetName() // 调用结构体USers的方法GetName
}
func main() {
    list := []Users{{"乔峰"}, {"慕容复"}, {"清风扬"}}
    for _, t := range list {
        t2 := t // 定义新变量t2 t赋值给t2
        defer t2.GetName()
    }
}

输出:清风扬 慕容复 乔峰。

通过以上例子,结合

我们可以得出下面的结论:

defer后面的语句在执行的时候,函数调用的参数会被保存起来,但是不执行。也就是复制了一份。但是并没有说struct这里的*指针如何处理,

通过这个例子可以看出go语言并没有把这个明确写出来的this指针(比如这里的* Users)当作参数来看待。到这里有滴朋友会说。看似多此一举的声明,

直接去掉指针调用 t *Users改成 t Users 不就行了?

package main

import "fmt"

type Users struct {
    name string
}

func (t Users) GetName() { // 注意这里是 * 传地址 引用Users
    fmt.Println(t.name)
}

func main() {
    list := []Users{{"乔峰"}, {"慕容复"}, {"清风扬"}}
    for _, t := range list {
        defer t.GetName()
    }
}

输出:清风扬 慕容复 乔峰。这就回归到上面的 defer 函数非引用调用的示例了。所以这里我们要注意defer后面的指针函数和普通函数的调用区别。很容易混淆出错。

未完待续。。。

Copyright 2022 版权所有 软件发布 访问手机版

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 联系我们