先抛砖引玉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后面的指针函数和普通函数的调用区别。很容易混淆出错。 多个 defer 注册,按 FILO 次序执行 ( 先进后出 )。哪怕函数或某个延迟调用发生错误,这些调用依旧会被执行,我们看看这一段 复制代码 package main func users(i int) { defer println("北丐") defer println("南帝") defer func() { println("西毒") println(10 / i) // 异常未被捕获,逐步往外传递,最终终止进程。 }() defer println("东邪") } func main() { users(0) println("武林排行榜,这里不会被输出哦") } 复制代码 输出: 复制代码 东邪 西毒 南帝 北丐 panic: runtime error: integer divide by zero goroutine 1 [running]: main.users.func1(0x0) 复制代码 我们发现函数中异常,最后才捕获输出,但是一旦捕获了异常,后面就不会再执行了,即终止了程序。 无论从事什么行业,只要做好两件事就够了,一个是你的专业、一个是你的人品,专业决定了你的存在,人品决定了你的人脉,剩下的就是坚持,用善良專業和真诚赢取更多的信任。https://www.cnblogs.com/phpper/p/11984161.html