返回

Go defer 盲区分析:从面试经验看隐藏的问题

后端

Go 语言中的 defer:及其使用中的盲区

简介

在 Go 语言中,defer 是一个强大的工具,允许我们在函数返回之前执行指定的代码块。它在处理资源释放、错误处理和其他需要在函数退出之前执行的任务的场景中非常有用。然而,使用 defer 时需要格外小心,因为存在一些盲区,如果不小心,很容易陷入其中。

defer 的执行顺序

defer 按照后进先出的原则执行,这意味着最后调用的 defer 将首先执行。

代码示例:

func main() {
  defer fmt.Println("world")
  defer fmt.Println("hello")
  fmt.Println("Go")
}

输出:

hello
world
Go

defer 中的变量作用域

defer 中的变量可以在整个函数体中访问,包括局部变量和参数。但是,在使用局部变量时需要注意,如果变量在 defer 执行时已经超出作用域,可能会导致程序崩溃。

代码示例:

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

输出:

panic: runtime error: index out of range

defer 中的函数调用

defer 中的函数调用是延迟执行的,这意味着函数不会立即执行,而是在 defer 返回后才执行。

代码示例:

func main() {
  defer func() { fmt.Println("world") }()
  fmt.Println("hello")
}

输出:

hello
world

defer 和异常处理

defer 中的代码会在函数返回前执行,即使函数在执行过程中遇到了异常。这对于处理资源释放等场景非常有用,但是也需要注意,如果 defer 中的代码抛出异常,该异常将不会被捕获,从而导致程序崩溃。

代码示例:

func main() {
  defer func() { fmt.Println(10 / 0) }()
  fmt.Println("hello")
}

输出:

hello
panic: runtime error: integer divide by zero

defer 和并发编程

defer 也可以在并发编程中使用。但是,需要注意的是,defer 中的代码是在当前 goroutine 中执行的,而不是在调用 defer 的 goroutine 中执行。因此,在 defer 中使用共享变量时需要格外小心,以避免并发安全问题。

代码示例:

func main() {
  x := 0
  go func() {
    for i := 0; i < 1000; i++ {
      x++
    }
  }()
  defer fmt.Println(x)
}

可能输出:

0

结论

defer 是一个强大的工具,可以简化 Go 语言中资源释放和其他常见任务的处理。但是,在使用 defer 时需要注意其执行顺序、变量作用域、函数调用、异常处理和并发编程等方面的盲区。通过了解这些盲区,我们可以避免陷阱,充分利用 defer 的强大功能。

常见问题解答

1. defer 可以用于哪些场景?

  • 资源释放
  • 错误处理
  • 确保特定操作在函数返回前执行

2. defer 中的代码是否可以访问函数体中的所有变量?

  • 是,defer 中的代码可以在整个函数体中访问局部变量和参数。

3. defer 中的函数调用是什么时候执行的?

  • defer 中的函数调用是延迟执行的,这意味着函数不会立即执行,而是在 defer 返回后才执行。

4. defer 中的代码会受到异常影响吗?

  • 不,defer 中的代码会在函数返回前执行,即使函数在执行过程中遇到了异常。

5. defer 在并发编程中如何工作?

  • defer 中的代码是在当前 goroutine 中执行的,而不是在调用 defer 的 goroutine 中执行。