• Golang开发--defer关键字


    defer是Go语言中的一个关键字,用于延迟执行函数或方法的调用,defer语句会将其后面的函数或方法调用推迟到当前函数返回之前执行,无论函数是正常返回还是发生异常。
    使用defer语句的方法如下:

    1. 在函数中使用defer语句时,需要在调用函数或方法的语句前面加上defer关键字。

    2. 可以使用多个defer语句,它们的执行顺序与声明顺序相反,即最后一个defer语句最先执行。

    3. defer语句中的函数或方法调用可以包含参数,这些参数会在defer语句执行时被计算并保存,但实际调用时会使用当前的参数值。

    4. defer 语句中的函数参数会在 defer 语句执行时求值,而不是在函数返回时求值。这意味着,如果 defer 语句中的函数调用有参数,参数的值在 defer 语句执行时就会被确定。

    func main() {
        defer fmt.Println("defer 1")
        defer fmt.Println("defer 2")
        fmt.Println("Hello, World!")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    输出结果为:

    Hello, World!
    defer 2
    defer 1
    
    • 1
    • 2
    • 3

    可以看到,"Hello, World!"是先输出的,而defer语句中的函数调用是在main函数返回之前执行的,且按照声明顺序的相反顺序执行。

    defer的作用主要有以下几个方面:

    1. 资源释放:defer语句常用于释放资源,比如关闭文件、关闭数据库连接等。通过在打开资源后使用defer语句,可以确保在函数返回之前一定会执行资源释放操作,避免资源泄漏。
    func readFile() {
        file, err := os.Open("filename.txt")
        if err != nil {
            fmt.Println("Error:", err)
            return
        }
        defer file.Close()
        // 读取文件并进行其他操作
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在上面的示例中,使用 defer 语句将 file.Close() 推迟到函数返回之前执行,无论函数返回时是正常执行还是发生错误,都能确保文件被关闭,避免资源泄漏。

    2.解锁操作:在使用互斥锁(mutex)或读写锁(RWMutex)的情况下,使用 defer 语句延迟解锁,以避免忘记解锁而导致死锁。例如:

    var mu sync.Mutex
    
    func someFunction() {
        mu.Lock()
        defer mu.Unlock()
        // 临界区代码
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在上面的示例中,使用 defer 语句将 mu.Unlock() 推迟到函数返回之前执行,确保在函数退出时解锁互斥锁。

    1. 错误处理:defer语句也可以用于处理错误。在函数中可能会发生多个错误,但只有最后一个错误需要处理,可以使用defer语句将错误处理代码推迟到函数返回之前执行,确保错误处理代码只会执行一次。
    func process() error {
        err := setup()
        if err != nil {
            return err
        }
        defer cleanup()
        // 其他操作
        return nil
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在上面的示例中,cleanup() 函数会在 process() 函数退出前被推迟执行,无论是正常返回还是发生错误。

    1. 日志记录:defer语句还可以用于记录日志。在函数中可能会有多个地方需要记录日志,但只需要在函数返回之前记录一次即可,可以使用defer语句将日志记录代码推迟到函数返回之前执行。

    5.defer 的执行顺序
    defer执行顺序和调用顺序相反,类似于栈后进先出(LIFO),defer在return之后执行,但在函数退出之前,defer可以修改返回值。

    func test() int {
    	i := 0
    	defer func() {
    		fmt.Println("defer1")
    	}()
    	defer func() {
    		i += 1
    		fmt.Println("defer2")
    	}()
    	return i
    }
    
    func main() {
    	fmt.Println("return", test())
    }
    // defer2
    // defer1
    // return 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    上面这个例子中,test返回值并没有修改,这是由于Go的返回机制决定的,执行Return语句后,Go会创建一个临时变量保存返回值。如果是有名返回(也就是指明返回值func test() (i int))

    func test() (i int) {
    	i = 0
    	defer func() {
    		i += 1
    		fmt.Println("defer2")
    	}()
    	return i
    }
    
    func main() {
    	fmt.Println("return", test())
    }
    // defer2
    // return 1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    这个例子中,返回值被修改了。对于有名返回值的函数,执行 return 语句时,并不会再创建临时变量保存,因此,defer 语句修改了 i,即对返回值产生了影响。

    有名返回(Named Return)和无名返回(Unnamed Return)是用于函数返回值的两种不同机制。

    有名返回无名返回
    定义有名返回是在函数定义时为返回值指定了变量名。可以在函数体内部直接使用这些变量名,并在函数结束时,直接通过 return 语句返回这些变量的值。无名返回是在函数定义时没有为返回值指定变量名,只指定了返回值的类型。函数体内部直接通过 return 语句返回计算结果,不需要使用具体的变量名来存储返回值。
    使用使用有名返回时,函数体内部可以在任何地方使用返回值的变量名,并对其进行修改或操作。在函数结束时,只需使用 return 语句返回这些变量名即可。使用无名返回时,返回值的计算通常在 return 语句中完成,并且该值不能在函数体内的其他地方使用或修改。
    优点可以在函数体内部多次修改返回值,灵活性更高。可以给返回值起一个有意义的变量名,增加代码的可读性和可维护性。简洁明了,减少了代码的复杂性。对于简单的函数,无名返回可以更加直观和高效。

    有名返回:

    func calculateSum(a, b int) (sum int) {
        sum = a + b
        return sum
    }
    
    • 1
    • 2
    • 3
    • 4

    无名返回:

    func calculateSum(a, b int) int {
        return a + b
    }
    
    • 1
    • 2
    • 3

    需要注意的是,在有多个返回值的函数中,有名返回和无名返回可以混合使用。例如,函数可以定义有名返回值和无名返回值的组合。

  • 相关阅读:
    3招学会TikTok电商选品,速看
    requests 在 Python 3.2 中使用 OAuth 导入失败的问题与解决方案
    C++ Day3
    平均负载与 CPU 使用率,到底有啥区别?
    C++ Primer学习笔记-----第十八章:用于大型程序的工具
    路漫漫远修兮-GeoServer2.16.0版本跨域解决
    HTML制作个人网页制作(简单静态HTML个人博客网页作品)
    Java中PDF文件传输有哪些方法?
    【CKA考试笔记】十八、StatefulSet
    Hutool集合相关工具类CollUtil常用方法
  • 原文地址:https://blog.csdn.net/liulanba/article/details/133136652