Golang之defer

2018-12-05 22:46:42

背景

defer在golang中属于关键词,主要用于资源释放,会在函数返回之前被调用,但其中也包含了很多的坑。下面我们通过几个常见例子来进行相关的讲解。

例子

// e.g.1
func f1() (r int) {
	defer func() {
		r = r + 5
	}()
	return 1
}

// e.g.2
func f2() (r int) {
	t := 5
	defer func() {
		t = t + 5
	}()
	return t
}

// e.g.3
func f3() (r int) {
	t := 5
	defer func(r int) {
		r = r + 5
	}(r)
	return t
}

// e.g.4
func f4() {
	t := 5
	defer fmt.Println("t1 =", t)
	t = 4
	defer fmt.Println("t2 =", t)
}

我们先不直接说出其中运行的结果,我们都简单的针对上面的例子在心中计算一下,并想想是为什么,下面我在写出其中的结果.

原理讲解

以下为我们执行的实际结果

func main() {

    fmt.Println("f1() =", f1())
    fmt.Println("f2() =", f2())
    fmt.Println("f3() =", f3())
    f4()
    
    // Outputs:
    // f1() = 6
    // f2() = 5
    // f3() = 5
    // t2 = 4
    // t1 = 5

    return
}

上面的结果和你心中想到的是否一致?哈哈,如果一致的话相信你是懂defer的运行机制的,可以绕过本文了。有不懂或没弄对的地方的话,就继续往下看,我来一一给你讲解。

defer规则

我们来直接说一下defer的三个简单规则,详见官方文档

  • 规则1-延迟函数的参数在defer语句出现时就已经确定下来了
  • 规则2-延迟函数执行按后进先出顺序执行,即先出现的defer最后执行
  • 规则3-延迟函数可能操作主函数的具名返回值


    针对规则1,意思是defer后面的函数一在定义完以后,如果函数有传入值,则在那一刻就把值给固定下来了,这个规则能很好的解释上面提到的f4(),打印的t1=5,t2=4


    针对规则2,意思是说defer采用的是FILO(先进后出)的规则,用这个规则也能解释f4()里面的打印顺序问题,先打印t2再打印t1


    针对规则3,意思是主函数可能有返回值,而这个defer后面的函数可能会影响到主函数的返回值,这里我们就使用规则3来对上面的另外三个例子进行一一说明,f1()里面return 1,哦,我再顺便说一个利于你理解记忆的小诀窍,在return的时候首先赋值,然后执行defer,最后返回,也就是先r=1,然后执行defer里面r = r + 5,最后才是返回的r,打印f1() = 6,解释通了吧?哈哈;f2()里面带有一点迷惑性,但是我们直接利用上面的规则拆分,return的时候是先执行的r = t,也就是r=5,而defer执行的操作只是对t操作,和r没有半毛钱的关系,这样打印f2() = 5也就能解释了;f3()里面同上面规则也一样,先赋值,但是有一点是defer里面函数通过值传递,在值传递的时候已经是进行了值的拷贝操作,并没有把返回的值给更改掉,或者换个思路,把f3()里面的defer函数r换成其他变量,也许一眼就看懂了;

小结

  • defer定义的延迟函数参数在defer语句出时就已经确定下来了
  • defer定义顺序与实际执行顺序相反
  • return不是原子操作,执行过程是: 保存返回值(若有)-->执行defer(若有)-->执行ret跳转申请资源后立即使用defer关闭资源是好习惯
201811读书汇

《第七天》 状态:100% finished 余华2013年作品,依然如《兄弟》、《活着》等带有批判意义是作品。但这次的这个似乎和以前作品的余华不太一样,总感觉少了些什么,活着说变了些什么。 全书以第一人称“我”来进行是叙事,我死掉后,灵魂出窍,开始进行第一天、第二天、...第七天的各种寻找,所见所想及回忆。广泛涉及官僚腐化、官民对立、贫富分化、道德沦丧、价值观混乱、暴力执法、食品安全、农村留守老人和儿童、城市鼠族等各阶层各方面的问题。 《皮囊》 状态:5% ing 朋友推荐,说是一本不错的好书,遂找来看看

Golang之go get golang.org/x timeout

背景 使用软件项目开发过程中是离不开开发环境的,而我们的Golang环境更不可能离开golang.org包,但我们在编译或者安装某个包时,常会提示 "golang.org/x/(ooo引用不同包,报的错会不一样)" (https fetch: Get https://golang.org/x/net/(ooo引用不同包,报的错会不一样)?go-get=1: dial tcp 216.239.37.1:443: i/o timeout) 毫无疑问,这是网络问题,这个地址在国内是不能轻易访问而导致下载不了,不知道谷歌老爹在重返China后还会不会有这个情况😆。 解决办法 有问题就有解决办法,毕竟办法总比问题多嘛,哈哈。在Github上面,Golang有个托管地址https://github.com/golang,里面相当于golang.org/