LiSheng's blog LiSheng's blog
首页
笔记
个人简历
随笔集
GitHub (opens new window)
首页
笔记
个人简历
随笔集
GitHub (opens new window)
  • golang

    • 并发编程

      • GMP并发模型
      • 锁相关
      • groutine并发相关
      • go如何实现原子操作
    • 内存管理

    • 数组和切片的区别
    • new和make
    • go defer
      • context
      • channel
      • go map
      • interface
      • 对象系统
      • rune 类型
      • 字符串拼接的几种方式
    • cplus

    • leetcode

    • 存储技术

    • 分布式系统

    • 计算机网络

    • Linux操作系统

    • Redis

    • 其他

    • 笔记
    • golang
    lisheng
    2024-09-10
    目录

    go defer

    # 1. defer 的基本概念

    • 问题: 请解释 Go 语言中的 defer 语句,以及它的执行时机。
    • 回答要点:
      • defer 语句用于延迟函数的执行,直到所在函数即将返回时才执行。
      • defer 语句的典型应用场景包括:资源清理(如文件关闭、解锁)、日志记录、异常处理等。
      • 多个 defer 语句按逆序(LIFO,后进先出)执行。

    # 2. defer 的参数求值

    • 问题: 请解释在 defer 中,函数参数何时求值?给出示例。
    • 回答要点:
      • defer 语句的参数在 defer 语句执行时就立即求值,而不是在延迟的函数调用时求值。
      • 示例:
        func main() {
            x := 5
            defer fmt.Println(x) // 这里的 x 的值是 5
            x = 10
            fmt.Println("x =", x)
        }
        // 输出:
        // x = 10
        // 5
        
        1
        2
        3
        4
        5
        6
        7
        8
        9

    # 3. defer 与命名返回值

    • 问题: 请解释 defer 与命名返回值的关系,特别是在 defer 语句中修改返回值的情况。
    • 回答要点:
      • 在 Go 中,带有命名返回值的函数会将返回值作为局部变量声明,并在 return 之前赋值。
      • defer 语句可以修改命名返回值。
      • 示例:
        func test() (x int) {
            defer func() { x++ }()
            return 1
        }
        // 输出:2
        
        1
        2
        3
        4
        5
      • 在这个例子中,x 是命名返回值,return 1 会将 x 设置为 1,然后执行 defer 中的匿名函数,使 x 增加 1。

    # 4. 多个 defer 的执行顺序

    • 问题: 当函数中有多个 defer 语句时,它们的执行顺序是什么?给出示例。
    • 回答要点:
      • 多个 defer 语句按逆序执行,即最后一个 defer 语句最先执行,第一个 defer 语句最后执行。
      • 示例:
        func main() {
            defer fmt.Println("First")
            defer fmt.Println("Second")
            defer fmt.Println("Third")
        }
        // 输出:
        // Third
        // Second
        // First
        
        1
        2
        3
        4
        5
        6
        7
        8
        9

    # 5. defer 与 panic 和 recover

    • 问题: defer 在遇到 panic 时的行为是什么?如何结合 defer 和 recover 来处理异常?
    • 回答要点:
      • 即使发生 panic,defer 语句仍然会执行,这是处理异常的最后机会。
      • recover 函数可以从 panic 中恢复,避免程序崩溃。
      • 示例:
        func main() {
            defer func() {
                if r := recover(); r != nil {
                    fmt.Println("Recovered from", r)
                }
            }()
            panic("something went wrong")
        }
        // 输出:
        // Recovered from something went wrong
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10

    # 6. defer 的常见陷阱

    • 问题: 请解释 defer 使用中的常见陷阱,如闭包捕获变量、循环中的 defer 等。
    • 回答要点:
      • 闭包捕获变量:defer 中的函数如果是闭包,会捕获外部变量的引用,而不是值。
      • 循环中的 defer:defer 在循环中定义时,会在函数返回时一次性执行,而不是每次循环迭代时立即执行。
      • 示例:
        func main() {
            for i := 0; i < 3; i++ {
                defer fmt.Println(i) // 输出 2 2 2,而不是 0 1 2
            }
        }
        
        1
        2
        3
        4
        5

    # 7. defer 与性能

    • 问题: defer 对性能有何影响?在什么情况下应避免使用 defer?
    • 回答要点:
      • defer 会增加一定的性能开销,因为它需要记录延迟函数调用并在函数返回时执行。
      • 在性能关键的代码路径中,特别是高频调用的情况下,应谨慎使用 defer。
      • 例如,在一个小函数中频繁使用 defer 关闭文件或释放锁可能导致性能下降。

    # 8. defer 与接口方法

    • 问题: 当 defer 调用接口方法时,有什么特别的注意事项?
    • 回答要点:
      • defer 执行时,接口的动态类型已经固定,因此接口方法调用也会按照定义时的类型执行。
      • 如果接口变量在 defer 语句之后被修改,那么 defer 执行的仍然是之前的接口方法。
      • 示例:
        type MyInterface interface {
            Method()
        }
        
        type MyStruct struct{}
        
        func (m *MyStruct) Method() {
            fmt.Println("Method called")
        }
        
        func main() {
            var i MyInterface = &MyStruct{}
            defer i.Method()
            i = nil // 修改接口变量
        }
        // 输出:Method called
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16

    # 9. defer 在高并发环境下的使用

    • 问题: 在高并发场景下,如何正确使用 defer 来确保资源的安全释放?
    • 回答要点:
      • 确保 defer 所使用的资源在 Goroutine 内部是独立的,不会被其他 Goroutine 竞争访问。
      • 对于需要锁定的资源,使用 sync.Mutex 和 defer 来保证锁的正确释放:
        var mu sync.Mutex
        func criticalSection() {
            mu.Lock()
            defer mu.Unlock()
            // critical code
        }
        
        1
        2
        3
        4
        5
        6
      • 在并发环境下,正确使用 defer 可以避免因忘记释放资源而引发的死锁或资源泄漏问题。

    # 10. defer 的最佳实践

    • 问题: 请总结使用 defer 的最佳实践。
    • 回答要点:
      • 使用 defer 来确保资源(如文件、网络连接等)的正确关闭。
      • 在函数开头使用 defer 声明清理操作,避免中途返回忘记清理。
      • 注意 defer 的参数求值时机,确保传递的是预期的值。
      • 在高性能场景下慎用 defer,可以手动调用清理函数以优化性能。
    编辑 (opens new window)
    上次更新: 2024/09/13, 11:59:12
    new和make
    context

    ← new和make context→

    最近更新
    01
    ceph分布式存储-对象存储(RGW)搭建
    10-27
    02
    ceph分布式存储-集群客户端连接
    10-27
    03
    ceph分布式存储-管理crushmap
    10-27
    更多文章>
    Theme by Vdoing
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式