每次defer都会将defer函数压入栈中,调用函数或者方法结束时,从栈中取出执行,所以多个defer的执行顺序是先入后出。
for i := 0; i <= 3; i++ {
defer fmt.Print(i)
}
//输出结果时 3,2,1,0
一、defer的触发时机
包裹着defer语句的函数返回时
包裹着defer语句的函数执行到最后时
当前goroutine发生Panic时
//输出结果:return前执行defer
func f1() {
defer fmt.Println("return前执行defer")
return
}
//输出结果:函数执行
// 函数执行到最后
func f2() {
defer fmt.Println("函数执行到最后")
fmt.Println("函数执行")
}
//输出结果:panic前 第一个defer在Panic发生时执行,第二个defer在Panic之后声明,不能执行到
func f3() {
defer fmt.Println("panic前")
panic("panic中")
defer fmt.Println("panic后")
}
二、defer,return,返回值的执行顺序
先来看3个例子
func f1() int { //匿名返回值
var r int = 6
defer func() {
r *= 7
}()
return r
}
func f2() (r int) { //有名返回值
defer func() {
r *= 7
}()
return 6
}
func f3() (r int) { //有名返回值
defer func(r int) {
r *= 7
}(r)
return 6
}
f1的执行结果是6, f2的执行结果是42,f3的执行结果是6
在golang的官方文档里面介绍了,return,defer,返回值的执行顺序:
if the surrounding function returns through an explicit return statement, deferred functions are executed after any result parameters are set by that return statement but before the function returns to its caller.
1. 先给返回值赋值
2. 执行defer语句
3. 包裹函数return返回
三、闭包与匿名函数
匿名函数:没有函数名的函数。
闭包:可以使用另外一个函数作用域中的变量的函数。
for i := 0; i <= 3; i++ {
defer func() {
fmt.Print(i)
}
}
//输出结果时 3,3,3,3
因为defer函数的i是对for循环i的引用,defer延迟执行,for循环到最后i是3,到defer执行时i就
是3
for i := 0; i <= 3; i++ {
defer func(i int) {
fmt.Print(i)
}(i)
}
//输出结果时 3,2,1,0
因为defer函数的i是在defer声明的时候,就当作defer参数传递到defer函数中
四、defer源码解析
defer的实现源码是在runtime.deferproc
然后在函数返回之前的地方,运行函数runtime.deferreturn。
先了解defer结构体:
type _defer struct {
siz int32
started bool
sp uintptr // sp at time of defer
pc uintptr
fn *funcval
_panic *_panic // panic that is running defer
link *_defer
}
func deferproc(siz int32, fn *funcval) { // arguments of fn follow fn
sp := getcallersp()
argp := uintptr(unsafe.Pointer(&fn)) + unsafe.Sizeof(fn)
callerpc := getcallerpc()
d := newdefer(siz)
if d._panic != nil {
throw("deferproc: d.panic != nil after newdefer")
}
d.fn = fn
d.pc = callerpc
d.sp = sp
switch siz {
case 0:
// Do nothing.
case sys.PtrSize:
*(*uintptr)(deferArgs(d)) = *(*uintptr)(unsafe.Pointer(argp))
default:
memmove(deferArgs(d), unsafe.Pointer(argp), uintptr(siz))
}
return0()
}
func newdefer(siz int32) *_defer {
var d *_defer
sc := deferclass(uintptr(siz))
gp := getg()
if sc < uintptr(len(p{}.deferpool)) {
pp := gp.m.p.ptr()
if len(pp.deferpool[sc]) == 0 && sched.deferpool[sc] != nil {
.....
d := sched.deferpool[sc]
sched.deferpool[sc] = d.link
d.link = nil
pp.deferpool[sc] = append(pp.deferpool[sc], d)
}
if n := len(pp.deferpool[sc]); n > 0 {
d = pp.deferpool[sc][n-1]
pp.deferpool[sc][n-1] = nil
pp.deferpool[sc] = pp.deferpool[sc][:n-1]
}
}
......
d.siz = siz
d.link = gp._defer
gp._defer = d
return d
}
deferreturn 从当前g取出_defer链表执行,每个_defer调用freedefer释放_defer结构体,并将该_defer结构体放入当前p的deferpool中。
测试代码
var mu sync.Mutex
func noDeferLock() {
mu.Lock()
mu.Unlock()
}
func deferLock() {
mu.Lock()
defer mu.Unlock()
}
func BenchmarkNoDefer(b *testing.B) {
for i := 0; i < b.N; i++ {
noDeferLock()
}
}
func BenchmarkDefer(b *testing.B) {
for i := 0; i < b.N; i++ {
deferLock()
}
测试结果:
BenchmarkNoDefer-4 100000000 11.1 ns/op
BenchmarkDefer-4 36367237 33.1 ns/op
defer性能不高,每次defer耗时20ns,,在一个func内连续出现多次,性能消耗是20ns*n,累计出来浪费的cpu资源很大的。
~~~~~~~~推荐阅读~~~~~~~~