扫码订阅《 》或入驻星球,即可阅读文章!

GOLANG ROADMAP

阅读模式

  • 沉浸
  • 自动
  • 日常
首页
Go学习
  • Go学院

    • Go小课
    • Go小考
    • Go实战
    • 精品课
  • Go宝典

    • 在线宝典
    • B站精选
    • 推荐图书
    • 精品博文
  • Go开源

    • Go仓库
    • Go月刊
  • Go下载

    • 视频资源
    • 文档资源
Go求职
  • 求职服务

    • 内推互助
    • 求职助力
  • 求职刷题

    • 企业题库
    • 面试宝典
    • 求职面经
Go友会
  • 城市
  • 校园
推广返利 🤑
实验区
  • Go周边
消息
更多
  • 用户中心

    • 我的信息
    • 推广返利
  • 玩转星球

    • 星球介绍
    • 角色体系
    • 星主权益
  • 支持与服务

    • 联系星主
    • 成长记录
    • 常见问题
    • 吐槽专区
  • 合作交流

    • 渠道合作
    • 课程入驻
    • 友情链接
author-avatar

GOLANG ROADMAP


首页
Go学习
  • Go学院

    • Go小课
    • Go小考
    • Go实战
    • 精品课
  • Go宝典

    • 在线宝典
    • B站精选
    • 推荐图书
    • 精品博文
  • Go开源

    • Go仓库
    • Go月刊
  • Go下载

    • 视频资源
    • 文档资源
Go求职
  • 求职服务

    • 内推互助
    • 求职助力
  • 求职刷题

    • 企业题库
    • 面试宝典
    • 求职面经
Go友会
  • 城市
  • 校园
推广返利 🤑
实验区
  • Go周边
消息
更多
  • 用户中心

    • 我的信息
    • 推广返利
  • 玩转星球

    • 星球介绍
    • 角色体系
    • 星主权益
  • 支持与服务

    • 联系星主
    • 成长记录
    • 常见问题
    • 吐槽专区
  • 合作交流

    • 渠道合作
    • 课程入驻
    • 友情链接
  • 课程介绍

    • 《Go语言面试题精讲》
  • Go基础

    • 1.Go 程序的基本结构?
    • 2. Go 有哪些关键字?
    • 3. Go 有哪些数据类型?
    • 4. Go 方法与函数的区别?
    • 5.Go 方法值接收者和指针接收者的区别?
    • 6. Go 函数返回局部变量的指针是否安全?
    • 7.Go 函数参数传递到底是值传递还是引用传递?
    • 8.Go defer关键字的实现原理?
    • 9.Go 内置函数make和new的区别?
  • Slice

  • Map

  • Channel

  • Mutex

  • Goroutine

  • 调度模型

  • 内存管理

  • 并发编程

扫码订阅《 》或入驻星球,即可阅读文章!

8.Go defer关键字的实现原理?


caspar
【点击观看视频】Go defer关键字的实现原理?

定义:

defer 能够让我们推迟执行某些函数调用,推迟到当前函数返回前才实际执行。defer与panic和recover结合,形成了Go语言风格的异常与捕获机制。

使用场景:

defer 语句经常被用于处理成对的操作,如文件句柄关闭、连接关闭、释放锁

优点:

方便开发者使用

缺点:

有性能损耗

实现原理:

Go1.14中编译器会将defer函数直接插入到函数的尾部,无需链表和栈上参数拷贝,性能大幅提升。把defer函数在当前函数内展开并直接调用,这种方式被称为open coded defer

源代码:

func A(i int) {
    defer A1(i, 2*i)
    if(i > 1) {
        defer A2("Hello", "eggo")
    }
    // code to do something
    return
}
func A1(a,b int) {
    //......
}
func A2(m,n string) {
    //......
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

编译后(伪代码):

func A(i int) {
        // code to do something
    if(i > 1){
       A2("Hello", "eggo")
    }
    A1(i, 2*i)
    return
}
1
2
3
4
5
6
7
8

代码示例:

  1. 函数退出前,按照先进后出的顺序,执行defer函数
package main

import "fmt"

// defer:延迟函数执行,先进后出
func main() {
    defer fmt.Println("defer1")
    defer fmt.Println("defer2")
    defer fmt.Println("defer3")
    defer fmt.Println("defer4")
    fmt.Println("11111")
}

// 11111
// defer4
// defer3
// defer2
// defer1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  1. panic后的defer函数不会被执行(遇到panic,如果没有捕获错误,函数会立刻终止)
package main

import "fmt"

// panic后的defer函数不会被执行
func main() {
    defer fmt.Println("panic before")
    panic("发生panic")
    defer func() {
        fmt.Println("panic after")
    }()
}

// panic before
// panic: 发生panic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  1. panic没有被recover时,抛出的panic到当前goroutine最上层函数时,最上层程序直接异常终止
package main

import "fmt"

func F() {
    defer func() {
        fmt.Println("b")
    }()
    panic("a")
}

// 子函数抛出的panic没有recover时,上层函数时,程序直接异常终止
func main() {
    defer func() {
        fmt.Println("c")
    }()
    F()
    fmt.Println("继续执行")
}

// b
// c
// panic: a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  1. panic有被recover时,当前goroutine最上层函数正常执行
package main

import "fmt"

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

func main() {
    defer func() {
        fmt.Println("c")
    }()
    F()
    fmt.Println("继续执行")
}

// 捕获异常: a
// b
// 继续执行
// c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26