golang——defer

defer是什么

        defer是Go语言提供的一种用于注册延迟调用的机制,让函数或语句可以在当前函数执行完毕后(包括通过return正常结束或者panic导致的异常结束)执行。

        defer与panic和recover结合,形成了go语言风格的异常与捕获机制

常用于一些成对操作的场景:

        打开连接/关闭连接;加锁/释放锁;打开文件/关闭文件等。

package main

import "os"

func main() {
	f, err := os.Open(filename)

	if err != nil {
		panic(err)

	}

	if f != nil {
		defer f.Close()
	}
}

优点:方便使用

缺点:有性能损耗

每次defer语句执行时,会把函数“压栈”,函数参数会被拷贝下来

当外层函数(非代码块,eg一个for循环)退出时,defer函数按照定义的逆序执行

如果defer执行的函数为nil,那么会在最终调用函数产生panic

defer参数

在defer函数定义时,对外部变量的引用是有两种方式的

1.作为函数参数

在defer定义时就把值传递给defer,并被cache起来

2. 作为闭包引用

在defer函数真正调用时根据整个上下文确定当前的值

闭包

闭包=函数+引用环境

go的所有匿名函数都是闭包

package main

import "fmt"

func main() {
	var a = Accumulator()
	fmt.Printf("%d\n", a(1))
	fmt.Printf("%d\n", a(10))
	fmt.Printf("%d\n", a(100))
	fmt.Println("------------------------")
	var b = Accumulator()
	fmt.Printf("%d\n", b(1))
	fmt.Printf("%d\n", b(10))
	fmt.Printf("%d\n", b(100))
}
func Accumulator() func(int) int {
	var x int
	return func(delta int) int {
		fmt.Printf("(%+v,%+v) - ", &x, x)
		x += delta
		return x
	}
}

运行结果

(0xc000016098,0) - 1    
(0xc000016098,1) - 11   
(0xc000016098,11) - 111 
------------------------
(0xc0000160f0,0) - 1    
(0xc0000160f0,1) - 11   
(0xc0000160f0,11) - 111 

闭包引用了x变量,a,b可看作2个不同的实例,实例之间互不影响。实例内部,x变量是同一个地址,因此具有“累加效应”。

defer易错场景

type number
int func (n number)print(){
	fmt.Println(n)
}

func (n*number)pprint(){
	fmt.Println(*n)
}


func main(){
	var n number
	defer n.print()
	defer n.pprint()
	defer func(){
		n.print()
	}()
	defer func (){
		n.pprint()
	}()
	n = 3

}

第四个defer语句是闭包,引用外部函数的n, 最终结果是3;

第三个defer语句同第四个;

第二个defer语句,n是引用,最终求值是3.

第一个defer语句,对n直接求值,开始的时候n=0, 所以最后是0;

defer拆解

return xxx

经过编译后变成三条指令

  1. 返回值 = xxx

  2. 调用defer函数

  3. 空的return

例一

package main

import "fmt"

func f() (r int) {
	t := 5
	defer func() {
		t = t + 5
	}()
	return t
}
func main() {
	fmt.Println(f())//5
}

拆解后

package main

import "fmt"

func f() (r int) {
	t := 5
	//1.赋值指令
	r = t
	//2.defer被插入到赋值与返回之间执行,返回值r未被修改过
	func() {
		t = t + 5
	}()
	//3.空的return指令
	return
}
func main() {
	fmt.Println(f())
}

例二

package main

import "fmt"

func f() (r int) {
	t := 5
	//1.赋值指令
	//2.这里改的t是之前传值传进去的t,不会改变要返回的t
	defer func(t int) {
		t = t + 5
	}(t)
	//3.空的return指令
	return t
}
func main() {
	fmt.Println(f())//5
}

package main

import (
	"errors"
	"fmt"
)

func f1() {
	var err error
	defer fmt.Println(err)
	err = errors.New("defer error")
	return
}
func f2() {
	var err error
	defer func() {
		fmt.Println(err)
	}()
	err = errors.New("defer error")
	return
}
func f3() {
	var err error
	defer func(err error) {
		fmt.Println(err)
	}(err)
	err = errors.New("defer error")
	return
}
func main() {
	f1()
	f2()
	f3()
}

运行结果:

<nil>
defer error
<nil>  

      执行顺序 1->2->3 

      第1,3个函数是因为作为函数参数,定义的时候就会求值,定义的时候err变量的值都是nil, 所以最后打印的时候都是nil.

        第2个函数的参数其实也是会在定义的时候求值,只不过,第2个例子中是一个闭包,它引用的变量err在执行的时候最终变成 defer error

defer配合recover

一次偶然的请求可能会触发某个bug, 这时用recover捕获panic, 稳住主流程,不影响其他请求。

recover()函数只在defer的上下文中才有效(且只有通过在defer中用匿名函数调用才有效),直接调用的话,只会返回 nil.

package main

import (
	"fmt"
	"os"
	"time"
)

func main() {
	defer fmt.Println("defer main")
	var user = os.Getenv("USER_")

	go func() {
		defer func() {
			fmt.Println("defer caller")
			if err := recover(); err != nil {
				fmt.Println("recover success. err", err)
			}
		}()

		func() {
			defer func() {
				fmt.Println("defer here")
			}()
			if user == "" {
				panic("should set user env.")
			}
			//此处不会执行
			fmt.Println("after panic")
		}()
	}()
	time.Sleep(100)
	fmt.Println("end of main function")
}

输出

defer here
defer caller
recover success. err should set user env.
end of main function                     
defer main 

注意:

panic后的defer不会被执行(遇到panic,如果没有捕获错误,函数会立即终止)

panic没有recover时,抛出的panic到当前goroutine最上层函数时,最上层程序直接异常终止

package main

import "fmt"

func F() {
	defer func() {
		fmt.Println("b")
	}()
	panic("a")
}
func main() {
	defer func() {
		fmt.Println("c")
	}()
	F()
	fmt.Println("继续执行")
}
/*
b
c                                                                       
panic: a  
*/

panic有被recover时,当前goroutine最上层函数正常执行

package main

import "fmt"

func F() {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("捕获异常", err)
		}
		fmt.Println("b")
	}()
	panic("a")
}
func main() {
	defer func() {
		fmt.Println("c")
	}()
	F()
	fmt.Println("继续执行")
}
/*
捕获异常 a
b       
继续执行
c  
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值