目录

go 关键字 defer 使用详解

go 关键字 defer 使用详解

简介

defer 是 Go 语言的一大特性,用 defer 可以来声明一些延迟函数,这些延迟函数会被放进队列里,在外层函数执行完成后会依次执行延迟函数:

  • 在函数、方法返回 return 之前执行
  • 可以同时设置多个 defer 函数,执行顺序遵循 FILO 先进后出 顺序
  • defer 会将运行结果存在栈中,等到 return 之后再推出来执行
  • 一般常用于资源清理、锁释放、文件资源关闭、关闭数据库连接、与 panic recover 结合使用等

一、场景一:基础使用

func main() {
    defer fmt.Println(1)  // 输出1
    x = 2
    fmt.Println(x)
    return
}

2

1

二、场景二:先进后出

func main() {
    defer fmt.Println(1)  // 输出1
    defer fmt.Println(2)  // 输出1
    x = 3
    fmt.Println(x)
    return
}

3

2

1

三、场景三:闭包使用

func main() {
  fmt.Println("res:", testDefer()) // res:3
}

func testDefer() int {
	var i = 1
	defer func() { // 将函数存储在栈上,最后执行的时候调用该函数,打印出i,所以i的值在后面的改动生效
		fmt.Println("defer2:", i) // i=3
	}()

	i++ // i=2
  defer fmt.Println("defer3:", i) // 将结果“2” 存储在栈上,执行时输出
  defer fmt.Println("defer4:", i+1) // 结果 3,但是不会影响 i 的值
	i++ //i=3
	return i                 // 可以看作 var rsp=i,return rsp,所以defer里改了i的值时没用的
}

结果: defer4: 3 defer3: 2 defer2: 3 res: 3

四、生产场景

  • 打印耗时
func ProcessJob() {
    start := time.Now()
    defer func() {
        fmt.Printf("耗时: %v", time.Since(start))
    }()
    
    // 复杂业务逻辑...
}
  • 资源关闭
func ProcessFileFixed() {
    f, _ := os.Open("data.log")
 		defer func(){
       _=f.Close() // 确保资源释放
    }
    // 业务逻辑...
}
  • 锁关闭
func ProcessJob() {
    var mu=sync.Mutex
    mu.Lock()
 		defer func(){
      mu.Unlock() // 解锁,避免死锁
    }
    // 业务逻辑...
}
  • 事务提交
func TransferMoney(db *sql.DB) error {
    var err error
    tx, err := db.Begin()
    if err!=nil{
      return err
    }
    defer func() {
      if err!=nil{
        tx.Rollback()
      }else{
        tx.Commit()
      }
    }()
    
    // 业务逻辑...
    return nil
}
  • panic 捕获
func ProcessJob() {
    start := time.Now()
    defer func() {
        if p := recover(); p != nil {
           // 打印堆栈日志
        }
    }()
    
    // 复杂业务逻辑...触发 panic
  
    return
}