当前位置: 首页> 汽车> 车展 > 深圳保障性住房规划_天津搜索引擎推广系统_aso优化榜单_seo综合检测

深圳保障性住房规划_天津搜索引擎推广系统_aso优化榜单_seo综合检测

时间:2025/7/12 6:58:04来源:https://blog.csdn.net/qq_63728673/article/details/146799578 浏览次数: 0次
深圳保障性住房规划_天津搜索引擎推广系统_aso优化榜单_seo综合检测

defer在开发中的应用

Go 语言中的 defer:从入门到精通

引言

如果你刚接触 Go 语言,第一次看到 defer 关键字,可能会有点疑惑:

func main() {defer fmt.Println("Hello")fmt.Println("World")
}

运行后输出:

World
Hello

乍一看,defer 似乎在“搞延迟执行的把戏”,但它的作用远不止如此。在实际开发中,defer 可以帮我们实现资源释放、错误处理等功能,写出更优雅的代码。

今天,我们就来全面剖析 defer,带你从入门到精通。


1. defer 的基础用法

defer 语句用于 延迟执行 一个函数,直到所在的函数即将返回时再执行。

1.1 最基本的使用方式

func main() {defer fmt.Println("执行了 defer")fmt.Println("函数执行中...")
}

输出:

函数执行中...
执行了 defer

可以看到,defer 语句会在 函数返回前 执行。

1.2 defer 的执行顺序(LIFO 规则)

如果有多个 defer,它们会 按照后进先出(LIFO, Last In First Out) 的顺序执行:

func main() {defer fmt.Println("1")defer fmt.Println("2")defer fmt.Println("3")
}

输出:

3
2
1

也就是说,最后 defer 的语句最先执行,类似于栈的结构

1.3 defer 绑定的参数

defer 语句在声明时就会对参数进行求值,而不是等到真正执行时才计算。

func main() {x := 10defer fmt.Println("defer x:", x)x = 20fmt.Println("main x:", x)
}

输出:

main x: 20
defer x: 10

defer 在声明时就“捕获”了 x 的值(10),即使后续 x 变成了 20,defer 执行时仍然使用 10。

如果 defer 需要使用“最终的值”,可以传入 指针闭包

func main() {x := 10defer func() { fmt.Println("defer x:", x) }()x = 20fmt.Println("main x:", x)
}

输出:

main x: 20
defer x: 20

由于 defer 绑定的是 闭包,所以 x 的最终值是 20。


2. defer 的应用场景

2.1 资源释放

在处理文件、数据库连接等资源时,defer 让代码更简洁,避免忘记释放资源。

func readFile(filename string) {file, err := os.Open(filename)if err != nil {fmt.Println("打开文件失败:", err)return}defer file.Close() // 确保文件在函数退出时关闭fmt.Println("读取文件...")
}

2.2 互斥锁解锁

var mu sync.Mutexfunc criticalSection() {mu.Lock()defer mu.Unlock()fmt.Println("执行关键代码区域")
}

这样,无论函数中途如何返回,互斥锁都会被正确释放,避免死锁。

2.3 计算执行时间(简单性能分析)

func trackTime() func() {start := time.Now()return func() {fmt.Println("执行时间:", time.Since(start))}
}func main() {defer trackTime()()time.Sleep(2 * time.Second)
}

2.4 defer 与 panic/recover 的配合

deferpanic 发生时仍然会执行,因此常用于 异常捕获,避免程序崩溃。

func protect() {if r := recover(); r != nil {fmt.Println("捕获 panic:", r)}
}func mayPanic() {defer protect() // 保护代码块panic("发生严重错误!") // 触发 panic
}func main() {mayPanic()fmt.Println("程序继续运行")
}
  1. recover() 只有在 defer 作用域内才能捕获 panic,否则 panic 仍然会导致程序崩溃。
  2. 多个 defer 仍然遵循 LIFO 规则,可以在多个 defer 里做不同的善后处理。

总结使用场景

场景示例
释放锁defer mu.Unlock()
关闭文件defer file.Close()
关闭网络连接defer conn.Close()
关闭数据库连接defer db.Close()
捕获 panicdefer recover()
计算执行时间defer time.Since(start)
多个 defer 逆序执行defer fmt.Println()
删除临时文件defer os.Remove()

defer进阶以及注意点

第一眼看到defer其实就是认为是一个延迟执行,但是在一些复杂情况下,defer 的行为可能并不像你想的那么直观,甚至可能导致 隐藏的 bug性能问题;再总结一下defer中可能会犯的错误。


1. defer 与 panic/recover

deferpanic 发生时仍然会执行,这意味着它可以用来拦截异常,防止程序直接崩溃。

func protect() {if r := recover(); r != nil {fmt.Println("捕获 panic:", r)}
}func mayPanic() {defer protect()panic("发生严重错误!")
}func main() {mayPanic()fmt.Println("程序继续运行")
}

关键点

  1. recover() 只有在 defer 作用域内才能捕获 panic,否则 panic 仍然会导致程序崩溃。
  2. 多个 defer 仍然遵循 LIFO 规则,可用于分层处理不同的异常情况。

2. defer 影响返回值的两种情况

情况 1:普通返回值不会被 defer 修改

func test() int {x := 10defer func() {x = 20}()return x
}func main() {fmt.Println(test()) // 输出?
}

输出:10(因为 return x 先执行defer 修改 x 但不会影响返回值)


情况 2:命名返回值可以被 defer 修改

func test() (x int) {x = 10defer func() {x = 20}()return
}func main() {fmt.Println(test()) // 输出?
}

输出:20(因为 x命名返回值defer 直接修改 x,最终返回 20)


3. defer 在循环中的性能问题

错误示范

func badLoop() {for i := 0; i < 1000000; i++ {defer fmt.Println(i)}
}

这会让 defer 在内存中积累 100 万个调用,导致 严重的性能问题

优化方案

func goodLoop() {for i := 0; i < 1000000; i++ {fmt.Println(i) // 直接执行,避免不必要的 defer}
}

适用场景

  • 仅在必要时使用 defer,比如 文件、数据库连接的释放

4. defer 与接口方法的坑

示例

type User struct {name string
}func (u *User) Print() {fmt.Println("User:", u.name)
}func main() {u := &User{name: "Alice"}defer u.Print()u.name = "Bob"
}

输出:Bob(因为 defer 绑定的是 u.Print(),而 u.namedefer 执行前已被修改)

如何绑定 defer 时的变量值?

defer func(name string) {fmt.Println("User:", name)
}(u.name) // 传入当前 name 值

这样 defer 绑定的就是 "Alice",不会受后续修改影响。


5. defer 关闭 channel 需要谨慎

错误示范

func main() {ch := make(chan int)defer close(ch) // ⚠️ 可能 panicch <- 1
}

正确方式:让 唯一的发送方 负责关闭 channel

func sender(ch chan int) {defer close(ch)ch <- 1
}func main() {ch := make(chan int)go sender(ch)fmt.Println(<-ch)
}

6. os.Exit(0) 会跳过所有 defer

错误示范

func main() {defer fmt.Println("这条语句不会执行")os.Exit(0) // 直接终止程序
}

正确方式

func main() {defer fmt.Println("程序即将退出")return // 让函数正常返回
}

总结

defer 使用中的注意要点,防止误用和错用:

  1. LIFO 执行顺序,最后声明的 defer 先执行。
  2. 参数绑定时机:普通参数在 defer 声明时绑定,闭包/指针 绑定最终值。
  3. defer 可配合 panic/recover 进行异常捕获,防止程序崩溃。
  4. 循环中慎用 defer,避免创建大量 defer,影响性能。
  5. 返回值可能会被 defer 修改,特别是命名返回值。
  6. defer 关闭 channel 需要谨慎,避免多次关闭导致 panic。
  7. os.Exit(0) 直接终止程序,跳过所有 defer

关键字:深圳保障性住房规划_天津搜索引擎推广系统_aso优化榜单_seo综合检测

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: