MDGSF Software Engineer

[GO] defer

2017-11-10
GO

一、在函数体执行结束后按照调用顺序的相反顺序逐个执行。

二、即使函数发生严重错误也会执行。

三、支持匿名函数的调用。

四、通常用于资源清理、文件关闭、解锁以及记录时间等操作。

五、通过与匿名函数配合可在 return 之后修改函数的计算结果。

六、如果函数体内某个变量作为 defer 时匿名函数的参数,则在定义 defer 时即已经获得了拷贝,否则则是引用某个变量的地址。

七、panic 可以在任何地方引发,但 recover 只有在 defer 调用的函数中有效。

八、个人测试,ctrl-C 和 killall 的时候,defer 没有被执行。

例子 1

package main

import "fmt"

func main() {
    fmt.Println("f1() = ", f1())
    fmt.Println("f2() = ", f2())

    //输出
    //f1() =  1
    //f2() =  0
}

func f1() (result int) {
    defer func() {
        result++
    }()
    return 0
}

func f2() (result int) {
    return 0
    defer func() {
        result++
    }()
    return 0
}

例子 2

package main

import (
    "log"
)

func main() {
    defer log.Println("11111111")
    defer log.Println("22222222")
    defer log.Println("33333333")
}

/*
输出:
2018/02/27 09:01:21 33333333
2018/02/27 09:01:21 22222222
2018/02/27 09:01:21 11111111
*/

例子 3

package main

import (
    "log"
)

func main() {
    defer log.Println("11111111")
    defer log.Println("22222222")
    defer log.Println("33333333")
    panic("I panic in here.")
}

/*
输出:
2018/02/27 09:01:21 33333333
2018/02/27 09:01:21 22222222
2018/02/27 09:01:21 11111111
panic: I panic in here.

goroutine 1 [running]:
main.main()
        e:/Go/GOPATH/src/jian/test/test.go:11 +0x172
exit status 2
*/

例子 4

package main

import (
    "log"
)

func main() {
    defer log.Println("11111111")
    f1()
    defer log.Println("55555555")
}

func f1() {
    defer log.Println("22222222")
    defer log.Println("33333333")
    panic("I panic in here.")
    defer log.Println("44444444") //unreachable code.
}

/*
输出:
$ go run test.go
2018/02/27 09:09:46 33333333
2018/02/27 09:09:46 22222222
2018/02/27 09:09:46 11111111
panic: I panic in here.

goroutine 1 [running]:
main.f1()
        e:/Go/GOPATH/src/jian/test/test.go:16 +0x10c
main.main()
        e:/Go/GOPATH/src/jian/test/test.go:9 +0x93
exit status 2
*/

例子 5

package main

import (
    "fmt"
)

func main() {
    for i := 0; i < 3; i++ {
        defer func(i int) {
            fmt.Println(i)
        }(i)
    }
}
/*
输出:
2
1
0
*/

例子 6

package main

import (
    "fmt"
)

func main() {
    for i := 0; i < 3; i++ {
        defer func() {
            fmt.Println(i)
        }()
    }
}
/*
输出:
3
3
3
*/

因为 i 没有作为参数传递进去,所以每个闭包函数都是引用同一个 i 变量。

例子 7

package main

func main() {
    test()
}

func test() {
    println("test start")
    defer println("test end")

    for i := 0; i < 5; i++ {
        println("i =", i)
        defer println("defer i =", i)
    }

    println("end of loop")
}
/*
输出:
test start
i = 0
i = 1
i = 2
i = 3
i = 4
end of loop
defer i = 4
defer i = 3
defer i = 2
defer i = 1
defer i = 0
test end
*/

这个输出说明了在循环体中的 defer 一样要等到函数结束了才会被执行,如果这个 defer 后面是关闭文件的话,那就会有大量的文件描述符被占用,这个时候就不能使用 defer。

测试题

看下是否完全弄懂 defer

package main

import "fmt"

func main() {
    var fs = [4]func(){}

    for i := 0; i < 4; i++ {
        defer fmt.Println("defer i = ", i)

        defer func() {
            fmt.Println("defer_closure i = ", i)
        }()

        fs[i] = func() {
            fmt.Println("closure i = ", i)
        }
    }

    for _, f := range fs {
        f()
    }
}

输出:

closure i =  4
closure i =  4
closure i =  4
closure i =  4
defer_closure i =  4
defer i =  3
defer_closure i =  4
defer i =  2
defer_closure i =  4
defer i =  1
defer_closure i =  4
defer i =  0

weixingongzhonghao

Comments

Content