Go语言中的defer关键字

时间:2025-03-04 08:44:42

官方文档中关于defer语句的解释:

defer语句延迟执行一个函数,该函数被推迟到当包含它的程序返回时(包含它的函数 执行了return语句/运行到函数结尾自动返回/对应的goroutine panic)执行。

每次defer语句执行时,defer修饰的函数的返回值和参数取值会照常进行计算和保存,但是该函数不会执行。等到上一级函数返回前,会按照defer的声明顺序倒序执行全部defer的函数。defer的函数的任何返回值都会被丢弃。

如果一个defer的函数值为nil,则这个defer的函数会在函数执行时panic(异常),而不会在defer语句执行时panic,例如:

func test() {

    f := func(){ ("Test") }

    defer f()

    f = nil    // 这里将函数的“值”设为nil,此时再尝试执行这个函数的时候

    defer f()

    defer ("Test1")

}

 

func main() {

    test()

}

执行结果:

Test1

Test

panic: runtime error: invalid memory address or nil pointer dereference

[signal 0xc0000005 code=0x0 addr=0x0 pc=0x451668]

 

goroutine 1 [running]:

panic(0x49aaa0, 0xc042004090)

        D:/Program Files/Go/src/runtime/:500 +0x1af

()

        D:/Programming/GoWork/:28 +0xfc

()

        D:/Programming/GoWork/:31 +0x1b

可以看到,第一个Test1正常输出了,说明设置f = nil后,defer f()并未直接panic,而是在test()返回之前,倒序执行到第二次f()的时候,抛出异常。同时Test也输出出来了,说明defer的方法在会在panic之前执行,不会因为panic了而停止执行,对比测试:

func test() {

    f := func(){ ("Test") }

    f()

    f = nil

    f()

    ("Test1")

}

 

func main() {

    test()

}

执行结果:

Test

panic: runtime error: invalid memory address or nil pointer dereference

[signal 0xc0000005 code=0x0 addr=0x0 pc=0x40107a]

 

goroutine 1 [running]:

panic(0x49aa20, 0xc042004090)

        D:/Program Files/Go/src/runtime/:500 +0x1af

()

        D:/Programming/GoWork/:26 +0x3a

()

        D:/Programming/GoWork/:31 +0x1b

exit status 2

可以看到Test1并未输出,原因是在执行("Test1")之前test()已经panic了。

 

插播一条panic的处理方法:

通过内置方法recover()可以捕获panicrecover()方法有一个返回值,如果recover()捕获到panic,则返回panic对象,否则返回nil。下面是官方文档中的一个recover()的例子,封装了一个方法来捕获panic(类似于try…catch…):

func test() {

    f := func(){ ("Test") }

    f()

    f = nil

    f()

    ("Test1")

}

 

func safeFunc(target func()) {

    defer func() {

        if x := recover(); x != nil {

            ("Runtime panic: ", x)

        }

    } ()

    target()

}

 

func main() {

    safeFunc(test)

}

执行结果:

Test

Runtime panic:  runtime error: invalid memory address or nil pointer dereference

可以使用panic()函数人工抛出一个panic

panic("Error Message")

 

 

回到defer的用法,下面举了几个例子来说明defer的用法:

示例1

func main() {

    for i := 0; i < 3; i++ {

        defer (i)

    }

}

输出:2 1 0

上面的代码等价于:

func main() {

    defer (0)    // 每次defer语句执行时,defer修饰的函数的返回值和参数取值会照常进行计算和保存

    defer (1)    // 同理

    defer (2)    // 同理

}

即:

func main() {

    (2)

    (1)

    (0)

}

 

示例2

func main() {

    for i := 0; i < 3; i++ {

        defer func() { (i) } ()

    }

}

输出:

3 3 3

上面的代码等价于:

func main() {

    i := 0

    defer func() { (i) } ()    // defer修饰的函数是闭包函数,而非里面的fmt语句,故里面的参数不变

    i++    // i = 1

    defer func() { (i) } ()

    i++    // i = 2

    defer func() { (i) } ()

    i++    // i = 3

}

即:

func main() {

    i := 0

    i++

    i++

    i++

    func() { (i) } ()

    func() { (i) } ()

    func() { (i) } ()

}

 

示例3

type demo1 struct {

    name string

}

func (t * demo1) close(){

    (," closed");

}

func main(){

    ts:=[]demo1{{"a"},{"b"},{"c"}}

    for _,t := range ts{

        defer  ()

    }

}

输出:

c  closed

c  closed

c  closed

上面的代码(main部分)等价于:

func main() {

    ts := demo1{{"a"}, {"b"}, {"c"}}

    t = ts[0]

    defer ()

    t = ts[1]

    defer ()

    t = ts[2]

    defer ()

}

即:

func main() {

    ts := demo1{{"a"}, {"b"}, {"c"}}

    t = ts[0]

    t = ts[1]

    t = ts[2]

    ()

    ()

    ()

}

 

示例4

修改一下示例3

type demo1 struct {

    name string

}

func (t * demo1) close(){

    (," closed");

}

func close(t demo1){

    ()

}

func main(){

    ts:=[]demo1{{"a"},{"b"},{"c"}}

    for _,t := range ts{

        defer close(t)

    }

}

输出:

c  closed

b  closed

a  closed

上面的代码等价于:

func main() {

    ts:=[]Test{{"a"},{"b"},{"c"}}

    t = ts[0]

    Close(ts[0])    // 与示例1原理相同

    t = ts[1]

    Close(ts[1])

    t = ts[2]

    Close(ts[2])

}

 

下面的几个例子会用到returndefer的关系,return语句其实并不具有原子性,return语句的拆分:

return val

等价于:

real_return_val = val

// refer statements

return

 

示例5

func test() int {

    var i int

    defer func() {

        i++

        ("[1]", i)

    } ()

    defer func() {

        i++

        ("[2]", i)

    } ()

    return i

}

func main() {

    ("[3]", test())

}

输出:

[2] 1

[1] 2

[3] 0

上面的代码(test部分)等价于:

func test() int {

    var i int

    i = 0

    ret := i    // ret = 0

    i++

    ("[2]", i)    // [2] 1

    i++

    ("[1]", i)    // [1] 2

    return ret    // ret = 0

}

 

示例6

func test() (i int) {

    defer func() {

        i++

        ("[1]", i)

    }()

    defer func() {

        i++

        ("[2]", i)

    }()

    return i

}

func main() {

    ("[3]", test())

}

输出:

[2] 1

[1] 2

[3] 2

上面的代码(test部分)等价于:

func test() (i int) {

    i = 0

    i++

    ("[2]", i)    // [2] 1

    i++

    ("[1]", i)    // [1] 2

    return i    // i = 2

}

 

示例7

func f() (result int) {

    defer func() {

        result++

    }()

    return 0

}

func main() {

    (f())

}

输出:

1

上面的代码(f部分)等价于:

func f() (result int) {

    result = 0

    func() { result++ }    // result = 1

    return

}

 

示例8

func f() (r int) {

     t := 5

     defer func() {

       t = t + 5

     }()

     return t

}

func main() {

    (f())

}

输出:

5

上面的代码(f部分)等价于:

func f() (r int) {

    t := 5

    r = t

    func() { t = t + 5 }()

    return

}

 

示例9

func f() (r int) {

    defer func(r int) {

          r = r + 5

          (r)

    }(r)

    r++

    return 1

}

func main() {

    (f())

}

输出:

5

1

上面的代码(f部分)等价于:

func f() (r int) {

    r++

    r = 1

    func(r int) {

        r = r + 5

        (r)    // 5

    }(0)

    return

}