Go defer 调试复杂函数
随着函数变得复杂,需要处理的错误也变多,维护清理逻辑变得越来越困难。而Go语言独有的 defer 机制可以让事情变得简单。
defer的用法
当defer语句被执行时,跟在defer后面的函数会被延迟执行。直到包含该defer语句的函数执行完毕时,defer后的函数才会被执行,不论包含defer语句的函数是通过return正常结束,还是由于panic导致的异常结 束。你可以在一个函数中执行多条defer语句,它们的执行顺序与声明顺序相反。
defer语句经常被用于处理成对的操作,如打开、关闭、连接、断开连接、加锁、释放锁。通过 defer机制,不论函数逻辑多复杂,都能保证在任何执行路径下,资源被释放。释放资源的defer应 该直接跟在请求资源的语句后。
示例
- 文件打开关闭
package ioutil
func ReadFile(filename string) ([]byte, error) {
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer f.Close()
return ReadAll(f)
}
- 加锁、释放锁 (读写map)
var mu sync.Mutex
var m = make(map[string]int)
func lookup(key string) int {
mu.Lock()
defer mu.Unlock()
return m[key]
}
调试复杂程序 之 利器
可以利用defer机制,调试复杂函数的进出口,打印trace。
示例
import (
"time"
"log"
)
func bigSlowOperation() {
defer trace("bigSlowOperation")() // 后边括号 是调用匿名函数 不能少
log.Printf("defer...")
// ...lots of work...
time.Sleep(3 * time.Second) // 模拟该程序耗时的工作
}
func trace(msg string) func() {
log.Printf("enter %s", msg) // 先执行
start := time.Now()
//time.Sleep(2 * time.Second)
return func() {// 延迟执行的是该函数体
log.Printf("exit %s (%s)", msg, time.Since(start))
}
}
// 2019/04/29 21:33:59 enter bigSlowOperation
// 2019/04/29 21:33:59 defer...
// 2019/04/29 21:34:02 exit bigSlowOperation (3.003679253s)
从执行结果看出,defer 延迟执行的是那个函数体。
注意:匿名返回值和命名返回值函数中的不同
func calc(i int) (result int){
defer func(){
result += i
fmt.Println("defer result", result)
}()
result = i
return
}
func calc1(i int) int{
var result int
defer func(){
result += i
fmt.Println("defer result", result)
}()
result = i
return result
}
func main() {
i := 1
fmt.Println("main", calc(i))
// fmt.Println("main", calc1(i))
}
calc(i) 执行:
defer result 2
main 2
calc1(i) 执行:
defer result 2
main 1
参考
- 《Go程序设计语言》