defer是goland中的一个关键词,用于延迟执行一个函数调用,通常用于在函数返回之前执行一些清理操作,无论函数是否发生错误。当函数中的defer语句被执行时,被延迟的函数调用会被推入一个栈中,然后在函数返回时按照后进先出(LIFO)的顺序执行。defer语句在函数中的位置并不重要,它们会在函数返回之前的任何时候都被执行。如下
package main
import (
"fmt"
)
func main() {
defer func() {
fmt.Println("第一个defer。")
}()
defer func() {
fmt.Println("第二个defer。")
}()
defer func() {
fmt.Println("第三个defer。")
}()
fmt.Println("主进程运行结束。")
}
上面案例会输出
主进程运行结束。
第三个defer。
第二个defer。
第一个defer。
defer的机制,在某些资源处理上是很有用的,例如在文件操作中,使用defer关闭文件,无论是否正常读取,都用defer关键字调用关闭操作。
package main
import (
"fmt"
"os"
)
func main() {
_, err := readFile("test.txt")
if err != nil {
fmt.Println("读取文件时发生错误:", err)
return
}
fmt.Println("主进程运行结束。")
}
func readFile(filename string) ([]string, error) {
file, err := os.Open(filename)
if err != nil {
return nil, err
}
// 确保文件在函数返回前被关闭
defer func() {
err := file.Close()
if err != nil {
fmt.Println("关闭文件时发生错误:", err)
} else {
fmt.Println("成功关闭文件:", err)
}
}()
lines := []string{}
return lines, nil
}
//输出
成功关闭文件:
主进程运行结束。
当开发中使用defer语句确保在函数返回之前释放锁,避免忘记释放锁导致死锁的情况。
package main
import (
"fmt"
"sync"
"sync/atomic"
"time"
)
func main() {
var mutex sync.Mutex
var counter int32
var wg sync.WaitGroup
num := 2
for i := 0; i < num; i++ {
wg.Add(1)
go func(id int) {
fmt.Printf("第 %d 个goroutine尝试进入临界区\n", id)
mutex.Lock()
defer func() {
mutex.Unlock()
atomic.AddInt32(&counter, 1) // 利用原子操作,增加计数器
wg.Done()
}()
fmt.Printf("第 %d 个goroutine 进入临界区\n", id)
// 在临界区执行操作,模拟等待时常,例如一些数据库的更新操作
time.Sleep(time.Second)
fmt.Printf("第 %d 个goroutine离开临界区\n", id)
}(i)
}
// 等待所有goroutine执行完毕
wg.Wait()
// 检查计数器的值,如果所有goroutine都正确释放了锁,计数器应该等于goroutine的数量
if atomic.LoadInt32(&counter) == int32(num) {
fmt.Println("所有锁都被正确释放")
} else {
fmt.Println("存在锁泄漏")
}
}
案例输出:
第 1 个goroutine尝试进入临界区
第 1 个goroutine 进入临界区
第 0 个goroutine尝试进入临界区
第 1 个goroutine离开临界区
第 0 个goroutine 进入临界区
第 0 个goroutine离开临界区
所有锁都被正确释放
因此,可以通过defer来确保资源的释放、锁的释放、等其他一些必要的清理操作,尽量避免忘记执行清理操作的风险。
发表评论