golang之defer
概述
对于资源释放,有很多不同的实现方式,不同语言也有不同的惯用方法。
扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄
- C语言 :手动管理
- Golang :defer
- Python :上下文管理器contexManager
- C++ : 作用域和析构函数
- Rust :所有权和drop trait
1 // Contents returns the file's contents as a string. 2 func Contents(filename string) (string, error) { 3 f, err := os.Open(filename) 4 if err != nil { 5 return "", err 6 } 7 defer f.Close() // f.Close will run when we're finished. 8 9 var result []byte 10 buf := make([]byte, 100) 11 for { 12 n, err := f.Read(buf[0:]) 13 result = append(result, buf[0:n]...) // append is discussed later. 14 if err != nil { 15 if err == io.EOF { 16 break 17 } 18 return "", err // f will be closed if we return here. 19 } 20 } 21 return string(result), nil // f will be closed if we return here. 22 }用上面的话翻译代码中的`defer f.Close()`就是“将f.Close()放在Contents函数的最后执行” 将`f.Close()`放在defer后面有两个好处:保证资源释放、离`Open`比较近不会忘了做。 这没什么好说的,golang必须显式释放资源。 细节 首先明确两个概念 - defer语句执行 将defer语句中函数调用安排在了当前函数结束前执行 - 函数调用执行 运行defer语句中的函数调用 参数求值 这里的参数求值指的是defer语句中函数调用的参数。 参数在defer语句执行求值,而不是在函数调用执行时求值。 - 又一个例子
func trace(s string) string { fmt.Println("entering:", s) return s } func un(s string) { fmt.Println("leaving:", s) } func a() { defer un(trace("a")) fmt.Println("in a") } func b() { defer un(trace("b")) fmt.Println("in b") a() } func main() { b() }
首先看函数b,因为参数是在defer语句执行时求值的,所以`trace("b")`要先被求值[先打印"b",再返回"b"],然后再往下执行,在函数b结束之前会调用`un("b")`. 同理如函数a。现在猜一下执行结果。
entering: b in b entering: a in a leaving: a leaving: b执行顺序 当有多个defer语句的时候,到底是谁的函数调用先执行呢?
1 for i := 0; i < 5; i++ { 2 defer fmt.Printf("%d ", i) 3 }
defer的函数调用按着后进先出(LIFO)的方式执行。大概猜一猜,每次defer都会将函数调用压入栈中,最后依次出栈执行。
SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。 最后 golang中defer也就主要用在资源管理上了。明确以上几点问题,应该问题不大了(吹牛ing)。
更多精彩