跳过正文

Go语言之defer

·709 字
目录

defer的用途
#

defer是在函数执行到最后时(return之前执行),多用于保证函数结束或panic前关闭响应的资源,如关闭文件句柄、恢复panic等 可以简化程序代码

关闭文件句柄

package main

import (
    "fmt"
    "os"
)

func main(){
    fp, err := os.Open("filename")
    if err != nil {
        fmt.Println(err)
        return
    }
    defer func(){
        fmt.Println("close file hanlder")
        fp.Close()
    }()

    fmt.Println("done")
}

恢复panic,防止程序崩溃

package main

import (
    "fmt"
)

func main() {

    defer func(){
        if err := recover(); err != nil {
            fmt.Printf("got panic : %v\n", err)
        }
    }()

    panic("this is a panic")
}

多个defer的执行顺序
#

多个defer是一种栈的数据结构,先入后出

package main

import (
    "fmt"
)

func main(){
    defer func(){
        fmt.Println("defer 1")
    }()
    defer func(){
        fmt.Println("defer 2")
    }()
    defer func(){
        fmt.Println("defer 3")
    }()
}

defer与return的执行顺序
#

首先return的实现逻辑如下

  • 第一步是给返回值赋值
    • 若为有名返回值则直接赋值
    • 若为无名返回值则先声明再赋值
  • 第二步调用RET返回指令并传入返回值,RET会检查defer是否存在,若存在则先执行defer语句
  • 最后RET携带返回值退出函数

return并不是一个原子操作,函数返回值与return返回值并不一定一致。defer、return、返回值三者的执行顺序是:return最先给返回值赋值,接着defer执行收尾工作,最后RET指令携带返回值退出函数

return非原子操作主要区分在函数返回值是有名还是无名

示例如下:

package main

import (
    "fmt"
)

func main(){
    fmt.Println(foo1()) // 1
    fmt.Println(foo2()) // 2
    fmt.Println(foo3()) // 1
    fmt.Println(foo4()) // 0
    fmt.Println(foo5()) // 0
}

// 有名返回值
func foo1() (x int) {
    defer func(){
        x++
    }()
    return x
}

// 有名返回值
func foo2() (x int) {
    defer func(){
        x++
    }()
    return 1
}

// 有名返回值
func foo3() (x int) {
    defer incr(x)

    return 1
}

// 无名返回值
func foo4() int {
    x := 0
    defer func(){
        x++
    }()

    return x
}

// 无名返回值
func foo5() int {
    x := 0
    defer incr(x)

    return x
}

func incr(x int) {
    x++
}

defer和panic的执行顺序
#

defer在panic之前执行

package main

import (
    "fmt"
)

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

    panic("panic")
}

defer声明时会先将参数值计算好
#

package main

import (
    "fmt"
)

func main(){
    x := 1
    defer func(x int){
        fmt.Printf("defer x: %d\n", x) // 1
    }(x)

    x++

    fmt.Printf("x: %d\n", x) // 2
}