基本特性与语法 执行顺序(LIFO) 多个defer
按后进先出(LIFO)顺序执行:
defer fmt.Println("A") // 最后执行 defer fmt.Println("B") // 第二执行 defer fmt.Println("C") // 最先执行 // 输出:C B A
参数求值时机 defer
注册时立即求值参数,后续变量修改不影响已注册的值:
x := 1 defer fmt.Println(x) // 输出1(注册时x=1) x = 2
执行时机 在return
赋值后、函数返回前执行,可修改具名返回值:
func demo() (result int) { defer func() { result += 100 }() return 1 // 实际返回101 }
主要使用场景 资源清理(最常见场景) 确保打开的文件、网络连接等资源被正确关闭
func readFile (filename string ) error { file, err := os.Open(filename) if err != nil { return err } defer file.Close() return nil }
解锁互斥锁 确保锁在函数退出时被释放
var mu sync.Mutexvar data = make (map [string ]string )func updateData (key, value string ) { mu.Lock() defer mu.Unlock() data[key] = value }
捕获和处理 panic 与 recover 配合使用实现 panic 恢复
func safeCall () { defer func () { if r := recover (); r != nil { fmt.Println("Recovered from panic:" , r) } }() riskyOperation() }
记录函数执行时间 测量函数执行时间
func longRunningTask () { defer func (start time.Time) { fmt.Printf("Execution time: %v\n" , time.Since(start)) }(time.Now()) }
修改返回值 在函数返回前修改命名返回值
func process (data string ) (result string , err error) { defer func () { if err != nil { result = "fallback value" } }() if somethingWrong { return "" , errors.New("error occurred" ) } return "processed" , nil }
高级用法 多个 defer 的执行顺序 多个 defer 按后进先出(LIFO)的顺序执行
func multipleDefers () { defer fmt.Println("First defer" ) defer fmt.Println("Second defer" ) defer fmt.Println("Third defer" ) fmt.Println("Function body" ) }
参数即时求值 defer 的参数在声明时立即求值
func parameterEvaluation () { i := 0 defer fmt.Println("Deferred value:" , i) i++ fmt.Println("Current value:" , i) }
闭包中的变量引用 使用闭包可以引用最新的变量值
func closureReference () { i := 0 defer func () { fmt.Println("Deferred closure:" , i) }() i = 1 i = 2 }
循环中的defer
直接捕获循环变量会导致引用同一地址:
for i := 0 ; i < 3 ; i++ { defer func () { fmt.Println(i) }() defer func (n int ) { fmt.Println(n) }(i) }
注意事项与最佳实践 循环中的 defer 在循环中使用 defer 可能导致资源未及时释放
func processFiles (filenames []string ) { for _, name := range filenames { file, err := os.Open(name) if err != nil { log.Println(err) continue } defer file.Close() } } func processFilesCorrect (filenames []string ) { for _, name := range filenames { func () { file, err := os.Open(name) if err != nil { log.Println(err) return } defer file.Close() }() } }
性能考虑 在性能敏感的代码中避免不必要的 defer
func criticalSection () { mu.Lock() mu.Unlock() }
错误处理 defer 中的错误常被忽略,需要特别处理
func writeFile (filename string ) error { file, err := os.Create(filename) if err != nil { return err } defer func () { if cerr := file.Close(); cerr != nil && err == nil { err = cerr } }() _, err = file.Write([]byte ("data" )) return err }
避免在 defer 中修改返回值 除非必要,避免在 defer 中修改返回值,这可能导致难以追踪的 bug
func confusing () (x int ) { defer func () { x++ }() return 1 }
defer 与 panic/recover 的交互 理解 defer 在 panic 情况下的执行顺序
func panicExample () { defer fmt.Println("First defer" ) defer func () { if r := recover (); r != nil { fmt.Println("Recovered:" , r) } }() defer fmt.Println("Second defer" ) panic ("something went wrong" ) }