16

defer ,panic,recover

 4 years ago
source link: https://studygolang.com/articles/28227
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

defer的执行顺序

多个defer出现的时候,它是一个“栈”的关系,也就是先进后出。一个函数中,写在前面的defer会比写在后面的defer调用的晚。

package main

import "fmt"

func main() {
    defer func1()
    defer func2()
    defer func3()
}

func func1() {
    fmt.Println("A")
}

func func2() {
    fmt.Println("B")
}

func func3() {
    fmt.Println("C")
}
//输出结果:C B A

defer和return的先后顺序

return之后的语句先执行,defer后的语句后执行。

package main

import "fmt"

func deferFunc() int {
    fmt.Println("defer func called")
    return 0
}

func returnFunc() int {
    fmt.Println("return func called")
    return 0
}

func returnAndDefer() int {

    defer deferFunc()

    return returnFunc()
}

func main() {
    returnAndDefer()
}
//输出
//return func called
//defer func called

defer 无名返回值 有名返回值

  • 函数返回值初始化

    该知识点不属于defer本身,但是调用的场景却与defer有联系,所以也算是defer必备了解的知识点之一。

    如 : func DeferFunc1(i int) (t int) {}

    其中返回值t int,这个t会在函数起始处被初始化为对应类型的零值并且作用域为整个函数。

package main

import "fmt"

func DeferFunc(i int) (t int) {

    fmt.Println("t = ", t)

    return 2
}

func main() {
    DeferFunc(10)
}
//输出
//t =  0
  • defer、return 和无名的返回值
package main

func main() {
    name := run()
    println("return: name = " + name) 
}

func run() (string) {
    name := "Paul"
    defer sayHello(&name)
    name = "John"
    return name
}

func sayHello(name *string) {
    *name = "George"
    println("Hello " + *name)
}
// 输出
// Hello George
// return: name = John
  • defer、return 和有名返回值
//传指针
package main

func main() {
    name := run()
    println("return: name = " + name)
}

func run() (x string) {
    name := "Paul"
    x = name
    defer sayHello(&x)
    name = "John"
    return name
}

func sayHello(name *string) {
    println("Hello " + *name)
    *name = "George"
    println("Hello " + *name)
}
//输出
//Hello John
//Hello George
//return: name = George

//传值
package main

func main() {
    name := run()
    println("return: name = " + name)
}

func run() (x string) {
    name := "Paul"
    x = name
    defer sayHello(x)
    name = "John"
    return name
}

func sayHello(name string) {
    println("Hello " + name)
    name = "George"
    println("Hello " + name)
}

//输出
//Hello Paul  
//Hello George 
//return: name = John

说明:defer,return 和返回值三者之间的执行逻辑

return最先执行,return负责将结果写入返回值中;接着defer开始执行一些收尾工作;最后函数携带当前返回值退出

defer 闭包

  • 闭包是什么

    闭包是由函数及其相关引用环境组合而成的实体,即

    闭包=函数+引用环境

一般的函数都有函数名,但是匿名函数就没有。匿名函数不能独立存在,但可以直接调用或者赋值于某个变量。匿名函数也被称为闭包,一个闭包继承了函数声明时的作用域。在Golang中,所有的匿名函数都是闭包。

有个不太恰当的例子,可以把闭包看成是一个类,一个闭包函数调用就是实例化一个类。闭包在运行时可以有多个实例,它会将同一个作用域里的变量和常量捕获下来,无论闭包在什么地方被调用(实例化)时,都可以使用这些变量和常量。而且,闭包捕获的变量和常量是引用传递,不是值传递。

package main

func main() {
    name := run()
    println("return: name = " + name)
}

func run() (string) {
    name := "Paul"
    aFun := func() {
        println("Hello " + name)
        name = "George"
        println("Hello " + name)
    }
    name = "John"
    aFun()
    bfunc:= func() {
        println("Hello " + name)
        name = "Bobbi"
        println("Hello " + name)
    }
    bfunc()
    return name
}
//输出
//Hello John
//Hello George
//Hello George
//Hello Bobbi
//return: name = Bobbi
  • defer与闭包
package main

func main() {
    name := run()
    println("return: name = " + name)
}

func run() (x string) {
    name := "Paul"
    aFun := func() {
        println("Hello " + x)
        x = "George"
        println("Hello " + x)
    }
    defer aFun()
    name = "John"
    return name
}
//输出
//Hello John
//Hello George
//return: name = George




package main

func main() {
    name := run()
    println("return: name = " + name)
}

func run() (x string) {
    name := "Paul"
    x = name
    aFun := func(x string) {
        println("Hello " + x)
        x = "George"
        println("Hello " + x)
    }
    defer aFun(x)
    name = "John"
    return name
}
//输出:
//Hello Paul
//Hello George
//return: name = John

defer panic recover

能够触发defer的是遇见return(或函数体到末尾)和遇见panic。

遇见panic的情况:遍历本协程的defer链表,并执行defer。在执行defer过程中:遇到recover则停止panic,返回recover处继续往下执行。如果没有遇到recover,遍历完本协程的defer链表后,向stderr抛出panic信息。

A. defer遇见panic,但是并不捕获异常的情况

package main

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

func main() {
    var user = os.Getenv("USER_")
    go func() {
        defer func() {
            fmt.Println("defer here")
        }()

        if user == "" {
            panic("should set user env.")
        }
    }()

    time.Sleep(1 * time.Second)
    fmt.Printf("get result %d\r\n", 1)
}
//输出
//defer here
//panic: should set user env.

说明:会发现defer中的字符串”defer here”打印出来了,而main流程中的”ger result”没有打印,说明panic坚守了自己的原则:

执行,且只执行,当前goroutine的defer

B.defer遇见panic,并捕获异常

package main

import (
    "fmt"
)

func main() {
    defer_call()

    fmt.Println("main 正常结束")
}

func defer_call() {

    defer func() {
        fmt.Println("defer: panic 之前1, 捕获异常")
        if err := recover(); err != nil {
            fmt.Println(err)
        }
    }()

    defer func() { fmt.Println("defer: panic 之前2, 不捕获") }()

    panic("异常内容") //触发defer出栈

    defer func() { fmt.Println("defer: panic 之后, 永远执行不到") }()
}

//输出
//defer: panic 之前2, 不捕获
//defer: panic 之前1, 捕获异常
//异常内容
//main 正常结束

欢迎关注我们的微信公众号,每天学习Go知识

FveQFjN.jpg!web

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK