关于Go的闭包、匿名函数和defer的结合使用案例分析


原文链接: 关于Go的闭包、匿名函数和defer的结合使用案例分析
func withLock(lk sync.Locker, fn func()) {
   lk.Lock()
   defer lk.Unlock() // in case fn panics
   fn()
}

Go里的“析构函数”defer关键字类似于PHP的__destruct(),用来标记最后执行的Go语句,一般用在资源释放、关闭连接等操作,会在函数关闭前调用。多个defer的定义与执行类似于栈的操作:先进后出,最先定义的最后执行。在defer的使用中,碰到过许多坑,尤其是在defer与匿名函数搭配使用的时候,下面用一个案例分析下。

package main

func main() {
	var fs = [4]func(){} //声明一个function类型的slice,长度为4

	for i := 0; i < 4; i++ {
		defer println("defer i= ", i)
		//3 2 1 0 这是一个i作为参数传进去的输出,因为i是int型,所以遵循一个规则值拷贝的传递,还有defer是倒序执行的,所以先后输出3,2,1,0,跟下面的defer交替执行4次
		defer func() { println("defer_closure i = ", i) }()
		//4 4 4 4 执行完下面的代码后,到了该defer了,这也是一个匿名函数,同样的也没有参数,也没有定义i,所以这也是个闭包,用的也是外面的i,所以先输出4,接着执行上面的defer,这样反复执行4次
		fs[i] = func() { println("closure i = ", i) }
		// 4 4 4 4 把相应的4个匿名函数存到function类型的slice里,因为这是个匿名函数,又没有参数,且也没有定义i,所以i就是外层函数的地址引用,就是for循环的i的地址,执行完for后i的值为4,所以输出4个4
	}

	for _, f := range fs {
		//用for循环对slice的调用,f为slice的值,即匿名函数,而f()则是对匿名函数的调用
		f()
	}
}

延迟调用用参数在注册时求值或复制,可用用指针或闭包 "延迟" 读取。

package main

func main() {
	x, y := 10, 20
	defer func(i int) {
		println("defer: x=", i, "y=", y) // y 闭包引用用 y=120
	}(x)	// x =10 被复制
	x += 10
	y += 100
	    println("       x=", x, "y =", y) // x=20 y=120
}

输出:

    x= 20 y= 120

defer: x= 10 y= 120

`