go defer延迟调用详解 |
您所在的位置:网站首页 › go语言defer详解 › go defer延迟调用详解 |
文章目录1. 简介2. 原则3. defer特性4. defer用途5. defer陷阱6. defer nil 函数7. 在错误的位置使用 defer(未通过验证) 1. 简介 defer关键字的作用是当外围函数返回之后才执行被推迟的函数。在文件输入输出操作中经常可以见到 defer关键字,因为它使您不必记住何时关闭已打开的文件:defer关键字调用文件关闭函数关闭已打开 的文件时,可以紧靠着文件打开函数之后 2. 原则defer函数在外围函数返回之后,以后进先出(LIFO)的原则执行。简单点 说,在一个外围函数中有3个defer函数: f1() 最先出现,然后 f2() ,最后 f3() ,当外围函数执行返回之后, f3() 最先被执行,接着是 f2() ,最后是 f1() 3. defer特性关键字 defer 用于注册延迟调用。这些调用直到 return 前才被执。因此,可以用来做资源清理。多个defer语句,按先进后出的方式执行。defer语句中的变量,在defer声明时就决定了。 4. defer用途关闭文件句柄锁资源释放数据库连接释放go语言 defer go 语言的defer功能强大,对于资源管理非常方便,但是如果没用好,也会有陷阱。 defer 是先进后出 这个很自然,后面的语句会依赖前面的资源,因此如果先前面的资源先释放了,后面的语句就没法执行了 $ cat defer1.go package mainimport "fmt"func main(){ var whatever [5]struct{} for i := range whatever { defer fmt.Println(i)}}[root@localhost defer]# go run defer1.go 43210defer 碰上闭包 $ cat defer2.go package mainimport "fmt"func main(){ var whatever [5]struct{} for i := range whatever { defer func(){ fmt.Println(i)}()}}[root@localhost defer]# go run defer2.go 44444其实go说的很清楚,我们一起来看看go spec如何说的 Each time a “defer” statement executes, the function value and parameters to the call are evaluated as usualand saved anew but the actual function is not invoked.也就是说函数正常执行,由于闭包用到的变量 i 在执行的时候已经变成4,所以输出全都是4. defer f.Close 这个大家用的都很频繁,但是go语言编程举了一个可能一不小心会犯错的例子. $ cat defer3.gopackage mainimport "fmt"type Test struct { name string}func (t *Test)Close(){ fmt.Println(t.name," closed")}func main(){ ts :=[]Test{{"a"},{"b"},{"c"}} for _, t := range ts { defer t.Close() }}[root@localhost defer]# go run defer3.go c closedc closedc closed这个输出并不会像我们预计的输出c b a,而是输出c c c 可是按照前面的go spec中的说明,应该输出c b a才对啊. 那我们换一种方式来调用一下. $ cat defer4.gopackage mainimport "fmt"type Test struct { name string}func (t *Test)Close(){ fmt.Println(t.name," closed")}func Close(t Test){ t.Close()}func main(){ ts :=[]Test{{"a"},{"b"},{"c"}} for _, t := range ts { defer Close(t) }}[root@localhost defer]# go run defer4.go c closedb closeda closed这个时候输出的就是c b a 当然,如果你不想多写一个函数,也很简单,可以像下面这样,同样会输出c b a 看似多此一举的声明 $ cat defer5.gopackage mainimport "fmt"type Test struct { name string}func (t *Test)Close(){ fmt.Println(t.name," closed")}func main(){ ts :=[]Test{{"a"},{"b"},{"c"}} for _, t := range ts { t2 := t defer t2.Close() }}[root@localhost defer]# go run defer5.go c closedb closeda closed通过以上例子,结合 Each time a “defer” statement executes, the function value and parameters to the call are evaluated as usualand saved anew but the actual function is not invoked. 这句话。可以得出下面的结论: defer后面的语句在执行的时候,函数调用的参数会被保存起来,但是不执行。也就是复制了一份。但是并没有说struct这里的this指针如何处理,通过这个例子可以看出go语言并没有把这个明确写出来的this指针当作参数来看待。 多个 defer 注册,按 FILO 次序执行 ( 先进后出 )。哪怕函数或某个延迟调用发生错误,这些调用依旧会被执行。 $ cat defer6.gopackage mainfunc test(x int){ defer println("a") defer println("b") defer func(){ println(100/ x)// div0 异常未被捕获,逐步往外传递,最终终止进程。 }() defer println("c")}func main(){ test(0)}[root@localhost defer]# go run defer6.go cbapanic: runtime error: integer divide by zerogoroutine 1 [running]:main.test.func1(0x0) /root/go/defer/defer6.go:6 +0x6fmain.test(0x0) /root/go/defer/defer6.go:9 +0x140main.main() /root/go/defer/defer6.go:11 +0x2aexit status 2 *延迟调用参数在注册时求值或复制,可用指针或闭包 “延迟” 读取。(未通过) package mainfunc test() func() { x, y :=10,20 defer func(i int){println("defer:", i, y)// y 闭包引用}(x)// x 被复制 x +=10 y +=100println("x =", x,"y =", y)}func main(){ test()}输出结果: x =20 y =120defer:10120*滥用 defer 可能会导致性能问题,尤其是在一个 “大循环” 里。 $ cat defer8.go package mainimport ("fmt""sync""time")var lock sync.Mutexfunc test(){ lock.Lock() lock.Unlock()}func testdefer(){ lock.Lock() defer lock.Unlock()}func main(){ func(){ t1 := time.Now() for i :=0; i |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |