Go defer使用
defer使用语法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | //defer后面必须是函数调用语句或方法调用语句,不能是其他语句,否则编译器会出错。 package main import ( "fmt" ) func foo(n int) int { defer n++ //defer fmt.Println(n) return n } func main() { var i int = 100 foo(i) } |
1 2 3 | # command-line-arguments expression in defer must be function call syntax error: unexpected ++ at end of statement |
defer
语句的用途
含有 defer
语句的函数,会在该函数将要返回之前,调用另一个函数。
defer后面的函数在defer语句所在的函数执行结束的时候会被调用;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | package main import ( "fmt" ) type person struct { firstName string lastName string } func (p person) fullName() { fmt.Printf( "%s %s" ,p.firstName,p.lastName) } func main() { p := person { firstName: "John" , lastName: "Smith" , } defer p.fullName() fmt.Printf( "Welcome " ) } |
在上面的程序里的第 11 行,a
的初始值为 5。在第 12 行执行 defer
语句的时候,由于 a
等于 5,因此延迟函数 printA
的实参也等于 5。接着我们在第 13 行将 a
的值修改为 10。下一行会打印出 a
的值。该程序输出:
value of a before deferred function call 10
value of a in deferred function 5
从上面的输出,我们可以看出,在调用了 defer
语句后,虽然我们将 a
修改为 10,但调用延迟函数 printA(a)
后,仍然打印的是 5。
defer 栈
当一个函数内多次调用 defer
时,Go 会把 defer
调用放入到一个栈中,随后按照后进先出(Last In First Out, LIFO)的顺序执行。
我们下面编写一个小程序,使用 defer
栈,将一个字符串逆序打印。
1 2 3 4 5 6 7 8 9 10 11 12 | package main import ( "fmt" ) func main() { name := "Naveen" fmt.Printf( "Orignal String: %s\n" , string(name)) fmt.Printf( "Reversed String: " ) for _, v := range []rune(name) { defer fmt.Printf( "%c" , v) }} |
1 2 | Orignal String: Naveen Reversed String: neevaN |
defer 的实际应用
- file对象打开后的自动关闭
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | func CopyFile(dstName, srcName string) (written int64, err error) { src, err := os.Open(srcName) if err != nil { return } defer src.Close() dst, err := os.Create(dstName) if err != nil { return } defer dst.Close() // other codes return io.Copy(dst, src) } |
在打开输入文件输出文件后,不管后面的代码流程如何影响,这两个文件能够被自动关闭。
- mutex对象锁住后的自动释放
1 2 3 4 5 6 | func foo(...) { mu.Lock() defer mu.Unlock() // code logic } |
确保mu锁能够在函数foo退出之后自动释放。
WaitGroup的退出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | package main import ( "fmt" "sync" ) type rect struct { length int width int } func (r rect) area(wg *sync.WaitGroup) { defer wg.Done() if r.length < 0 { fmt.Printf( "rect %v's length should be greater than zero\n" , r) return } if r.width < 0 { fmt.Printf( "rect %v's width should be greater than zero\n" , r) return } area := r.length * r.width fmt.Printf( "rect %v's area %d\n" , r, area) } func main() { var wg sync.WaitGroup r1 := rect{-67, 89} r2 := rect{5, -67} r3 := rect{8, 9} rects := []rect{r1, r2, r3} for _, v := range rects { wg.Add(1) go v.area(&wg) } wg.Wait() fmt.Println( "All go routines finished executing" ) } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?