说一说Go中的延迟函数defer

简单的说就是Go中用defer关键字来修饰函数起到延迟执行的效果。

defer 表达式会被放入一个类似于栈( stack )的结构,所以调用的顺序是先进后出/后进先出的

package main

import (
    "fmt"
)

func main() {
    defer fmt.Print(1)
    defer fmt.Print(2)
    defer fmt.Print(3)
    defer fmt.Print(4)
}
执行结果:
4
3
2
1

因此这段代码输出的结果是 4321 而不是 1234 。

defer声明时会先计算确定参数的值,defer推迟执行的仅是其函数体

package main

import (
    "fmt"
    "time"
)

func main() {
    c := 2
    defer P(c)
    time.Sleep(5e9)
    c = 5
    fmt.Println("main ", c)
}

func P(c int) {
    fmt.Println("defer", c)
    fmt.Println("P    ", c)
}
执行结果: 
main  5
defer 2
P     2
package main

import (
    "fmt"
    "time"
)

func main() {
    defer P(time.Now())
    time.Sleep(5e9)
    fmt.Println("main ", time.Now())
}

func P(t time.Time) {
    fmt.Println("defer", t)
    fmt.Println("P    ", time.Now())
}

执行结果:
main  2018-07-25 16:42:19.228832898 +0800 HKT m=+5.003306219
defer 2018-07-25 16:42:14.226136368 +0800 HKT m=+0.000609813
P     2018-07-25 16:42:19.229130521 +0800 HKT m=+5.003603820

匿名返回值的情况

package main

import (
    "fmt"
)

func main() {
    fmt.Println("a return:", a())
}

func a() int {
    var i int
    defer func() {
        i++
        fmt.Println("a defer1:", i)
    }()
    defer func() {
        i++
        fmt.Println("a defer2:", i)
    }()
    return i
}
执行结果:
a defer2: 1
a defer1: 2
a return: 0

有名返回值的情况

package main

import (
    "fmt"
)

func main() {
    fmt.Println("a return:", a())
}

func a() (i int) {
    defer func() {
        i++
        fmt.Println("a defer1:", i)
    }()
    defer func() {
        i++
        fmt.Println("a defer2:", i)
}()
defer func() {
        i++
        fmt.Println("a defer3:", i)
    }()
    return i //或者等价于return
}

执行结果:
a defer3: 1
a defer2: 2
a defer1: 3
a return: 3
package main

import (
    "fmt"
)

func main() {
    c:=c()
    fmt.Println("c return:", *c, c)

func c() *int {
    var i int
    defer func() {
        i++
        fmt.Println("c defer1:", i, &i)
    }()
    defer func() {
        i++
        fmt.Println("c defer2:", i, &i)
}()
defer func() {
        i++
        fmt.Println("c defer3:", i, &i)
    }()

    return &i
}
执行结果: 
c defer3: 1 0xc420012088
c defer2: 2 0xc420012088
c defer1: 3 0xc420012088
c return: 3 0xc420012088

虽然 c()int 的返回值没有被提前声明,但是由于 c()int 的返回值是指针变量,那么在 return 将变量 i 的地址赋给返回值后,defer 再次修改了 i 在内存中的实际值,因此 return 调用 RET 退出函数时返回值虽然依旧是原来的指针地址,但是其指向的内存实际值已经被成功修改了

defer 的作用域

defer 只对当前协程有效(main 可以看作是主协程)

package main

import (
    "errors"
    "fmt"
    "time"
)

func main() {
    e := errors.New("error")
    fmt.Println(e)
    defer fmt.Println("defer")
    go func() { panic(e) }() // 会导致 defer 不会执行
    time.Sleep(1e9)
    fmt.Println("over.")
}
执行结果:
error
panic: error

goroutine 5 [running]:
main.main.func1(0x51b120, 0xc42000e1d0)
	/root/workspace/defer6.go:13 +0x3e
created by main.main
	/root/workspace/defer6.go:13 +0x145
exit status

任意一条(主)协程发生 panic 时,会执行当前协程中 panic之前已声明的 defer

package main

import (
    "errors"
    "fmt"
    "time"
)

func main() {
    e := errors.New("error")
    fmt.Println(e)
    defer fmt.Println("defer")
    panic(e) // defer 会执行
    time.Sleep(1e9)
    fmt.Println("over.")
}
执行结果:
error
defer
panic: error

goroutine 1 [running]:
main.main()
	/root/workspace/defer6.go:13 +0x178
exit status 2

发生 panic 的(主)协程中,如果没有一个 defer 调用 recover()进行恢复,则会在执行完最后一个已声明的 defer 后,引发整个进程崩溃

package main

import (
    "errors"
    "fmt"
    "time"
)

func main() {
    e := errors.New("error")
    fmt.Println(e)
    panic(e) // defer 不会执行
    defer fmt.Println("defer")
    time.Sleep(1e9)
    fmt.Println("over.")
}
执行结果:
error
panic: error

goroutine 1 [running]:
main.main()
	/root/workspace/defer6.go:12 +0xfb
exit status 2

动调用 os.Exit(int) 退出进程时,defer 将不再被执行

package main

import (
    "errors"
    "fmt"
    "time"
    "os"
)

func main() {
    e := errors.New("error")
    fmt.Println(e)
    os.Exit(1) // defer 不会执行
    defer fmt.Println("defer")
    time.Sleep(1e9)
    fmt.Println("over.")
}
执行结果:
error
exit status 1
package main

import (
    "errors"
    "fmt"
    "time"
    "os"
)

func main() {
    e := errors.New("error")
    fmt.Println(e)
    defer fmt.Println("defer")
    time.Sleep(1e9)
    fmt.Println("over.")
    os.Exit(1) // defer 不会执行
}
执行结果:
error
over.
exit status 1

 

你可能感兴趣的:(go)