使用延迟执行语句在函数退出时释放资源
处理业务或逻辑中涉及成对的操作是一件比较烦琐的事情,比如打开和关闭文件、接收请求和回复请求、加锁和解锁等。在这些操作中,最容易忽略的就是在每个函数退出处正确地释放和关闭资源。
defer 语句正好是在函数退出时执行的语句,所以使用 defer 能非常方便地处理资源释放问题。
1) 使用延迟并发解锁
在下面的例子中会在函数中并发使用 map,为防止竞态问题,使用 sync.Mutex 进行加锁,参见下面代码:
- var (
- // 一个演示用的映射
- valueByKey = make(map[string]int)
- // 保证使用映射时的并发安全的互斥锁
- valueByKeyGuard sync.Mutex
- )
-
- // 根据键读取值
- func readValue(key string) int {
- // 对共享资源加锁
- valueByKeyGuard.Lock()
- // 取值
- v := valueByKey[key]
- // 对共享资源解锁
- valueByKeyGuard.Unlock()
- // 返回值
- return v
- }
代码说明如下:
- 第 3 行,实例化一个 map,键是 string 类型,值为 int。
- 第 5 行,map 默认不是并发安全的,准备一个 sync.Mutex 互斥量保护 map 的访问。
- 第 9 行,readValue() 函数给定一个键,从 map 中获得值后返回,该函数会在并发环境中使用,需要保证并发安全。
- 第 11 行,使用互斥量加锁。
- 第 13 行,从 map 中获取值。
- 第 15 行,使用互斥量解锁。
- 第 17 行,返回获取到的 map 值。
使用 defer 语句对上面的语句进行简化,参考下面的代码。
- func readValue(key string) int {
-
- valueByKeyGuard.Lock()
-
- // defer后面的语句不会马上调用, 而是延迟到函数结束时调用
- defer valueByKeyGuard.Unlock()
-
- return valueByKey[key]
- }
上面的代码中第 6~8 行是对前面代码的修改和添加的代码,代码说明如下:
- 第 6 行在互斥量加锁后,使用 defer 语句添加解锁,该语句不会马上执行,而是等 readValue() 函数返回时才会被执行。
- 第 8 行,从 map 查询值并返回的过程中,与不使用互斥量的写法一样,对比上面的代码,这种写法更简单。