52.蛤蟆笔记go语言——defer使用场景
1. 简化资源的回收
比如:
mu.Lock()
defer mu.Unlock()
当然, defer 也有一定的开销, 也有为了节省性能而回避使用的 defer 的.
从简化资源的释放角度看, defer 类似一个语法糖, 好像不是必须的.
2. panic异常的捕获
defer 除了用于简化资源的释放外, 还是Go语言异常框架的一个组成部分.
Go语言中, panic用于抛出异常, recover用于捕获异常. recover只能在defer语句中使用, 直接调用recover是无效的.
比如:
packagemain
import"fmt"
funcmain(){
f()
fmt.Println("Returnednormallyfromf.")
}
funcf(){
deferfunc(){
ifr:=recover();r!=nil{
fmt.Println("Recoveredinf",r)
}
}()
fmt.Println("Callingg.")
g()
fmt.Println("Returnednormallyfromg.")
}
funcg(){
panic("ERROR")
}
因此, 如果要捕获Go语言中函数的异常, 就离不开defer语句了.
3.修改返回值
defer 除了用于配合 recover, 用于捕获 panic 异常外, 还可以用于在 return 之后修改函数的返回值.
例如:
func doubleSum(a, b int) (sum int) {
deferfunc() {
sum *=2
}()
sum = a + b
}
4.安全的回收资源
defer 最常见的用法是简化资源的回收. 而且, 从资源回收角度看, defer 只是一个语法糖.
比如, 有一个线程安全的slice修改函数, 为了性能没有使用defer语句:
func set(mu *sync.Mutex, arr []int, i, v int) {
mu.Lock()
arr[i] = v
mu.Unlock()
}
但是, 如果 i >= len(arr)的话, runtime就会抛出切片越界的异常(这里只是举例, 实际开发中不应该出现切片越界异常). 这样的话, mu.Unlock() 就没有机会被执行了.
如果用defer的话, 即使出现异常也能保证mu.Unlock()被调用:
func set(mu *sync.Mutex, arr []int, i, v int) {
mu.Lock()
defermu.Unlock()
arr[i] = v
}
当然, Go语言约定异常不会跨越package边界. 因此, 调用一般函数的时候不用担心goroutine异常退出的情况.
有疑问加站长微信联系(非本文作者)