GO-defer语句

概述

defer是go语言的一个流程控制语句,只能在函数和方法中使用,被defer调用的函数称为延迟函数。 格式:

defer fmt.Println("The finishing touches .")

为什么叫延长函数,有这样的规则:

  1. 包含defer的函数中所有语句被执行完,defer调用的函数被执行,才算此函数执行结束
  2. 如果有return,需要defer调用结束,才返回值
  3. 当出现panic时,defer被执行,才会扩散到调用方。

示例1

看起来有点像java中的finally,在return和抛出异常后也一样会执行,可用于资源的关闭。但也不太一样,下面看一个示例:

func main() {
	fmt.Println(isPositiveEventNumber(1))
}
func isPositiveEventNumber(number int) (result bool) {
	defer fmt.Println("done1..")
	fmt.Println("start...")
	if number< 0 {
		panic(errors.New("The number is a negative number!"))
	}
	if number%2 == 0 {
		return true
	}
	// 匿名函数
	defer func() {
		fmt.Println("done2..")
	}()
	return
}

input: 1

start… done2.. done1.. false

input: 2

start… done1.. true

input: -1

start… panic: The number is a negative number! done1.. …

输入1时,done2在done1之前输出,它是按照相反顺序输出。输入2时,done2并没有执行,因为return执行后就结束了,defer相当于放入了一个后进先出的队列,done2没有被放入队列。同样,在输入3时,panic执行后,还要执行defer。

示例2

func main() {
	recode()
}
func begin(funcName string) string {
	fmt.Println("function being")
	return funcName
}
func end(funcName string) string{
	 fmt.Println("function end")
	 return funcName
}
func recode() {
	defer end(begin("recode"))
	fmt.Println("function recode")
}

function being function recode function end

为什么会先执行begin,因为它的值作为end的参数,先求职。对于参数的问题,再看一个示例:

func main() {
	for i:=0; i<5; i++ {
		defer func() {
			fmt.Printf("%d ",i)
		}()
	}
}

5 5 5 5 5

为什么输出的不是4 3 2 1 0呢?因为这个匿名函数没有参数,不会先求值。如果把i作为参数,就会得到想要的结果:

defer func(i int) {
		fmt.Printf("%d ",i)
}(i)

也因此,使用要特别小心,如果延迟函数使用外部变量,最好作为参数传入。如果延迟函数return修改了外部值,有可能导致return结果的改变:

func c() (i int) {
	defer func() { i++ }()
	return 1
}

返回值为2。


参考书目: go并发编程实战

CONTENTS