Ezreal 书架 Ezreal 书架
Home
  • 《Go程序员面试笔试宝典》
  • 《RabbitMQ 实战指南》
  • 《深入理解kafka》
  • MySQL45讲
  • 透视HTTP协议
  • 结构化数据的分布式存储系统
  • Raft 共识算法
Home
  • 《Go程序员面试笔试宝典》
  • 《RabbitMQ 实战指南》
  • 《深入理解kafka》
  • MySQL45讲
  • 透视HTTP协议
  • 结构化数据的分布式存储系统
  • Raft 共识算法
  • 逃逸分析

  • 延迟语句

    • 延迟语句是什么
    • 延迟语句的执行顺序是什么
    • 如何拆解延迟语句
    • 如何确定延迟语句的参数
    • 闭包是什么
    • 延迟语句如何配合恢复语句
    • defer 链如何被遍历执行
    • 为什么无法从父 goroutine 恢复子 goroutine 的 panic
  • 数据容器

  • 通道

  • 接口

  • unsafe

  • context

  • Go程序员面试笔试宝典
  • 延迟语句
ezreal_rao
2023-04-27

如何拆解延迟语句

如果 defer 像前面介绍的那样简单,这个世界就完美了。但事情总是没这么简单,defer 用得 不好,会陷入泥潭。

避免陷入泥潭的关键是必须深刻理解下面这条语句:

return xxx
1

上面这条语句经过编译之后,实际上生成了三条指令:
1)返回值 = xxx。
2)调用 defer 函数。
3)空的 return。

第 1 和第 3 步是 return 语句生成的指令,也就是说 return 并不是一条原子指令;第 2 步是 defer 定义的语句,这里可能会操作返回值,从而影响最终结果。

下面来看两个例子,试着将 return 语句和 defer 语句拆解到正确的顺序。

第一个例子:

func f() (r int) {
	t := 5
	defer func() {
		t = t + 5
	}()
	return t
}
1
2
3
4
5
6
7

拆解后:

func f() (r int) {
    t := 5
    // 1. 赋值指令
    r = t
    // 2. defer 被插入到赋值与返回之间执行,这个例子中返回值 r 没被修改过
    func() {
        t = t + 5
    }
    // 3. 空的 return 指令
    return
}
1
2
3
4
5
6
7
8
9
10
11

这里第二步实际上并没有操作返回值 r,因此,main 函数中调用 f () 得到 5。

第二个例子:

func f2() (r int) {
	defer func(r int) {
		r = r + 5
	}(r)
	return 1
}
1
2
3
4
5
6

拆解后:

func f() (r int) {
    // 1. 赋值
    r=1
    // 2. 这里改的 r 是之前传进去的 r,不会改变要返回的那个 r 值
    func(r int) {
        r=r + 5
    }(r)
    // 3. 空的 return
    return
}
1
2
3
4
5
6
7
8
9
10

第二步,改变的是传值进去的 r,是形参的一个复制值,不会影响实参 r。因此,main 函数中需要调用 f () 得到 1。

#延迟语句#defer
上次更新: 5/9/2023, 10:58:32 AM
延迟语句的执行顺序是什么
如何确定延迟语句的参数

← 延迟语句的执行顺序是什么 如何确定延迟语句的参数→

最近更新
01
为什么我的MySQL会抖一下
07-15
02
HTTP 性能优化面面观
07-12
03
WebSocket:沙盒里的 TCP
07-12
更多文章>
Theme by Vdoing | Copyright © 2022-2024 Ezreal Rao | CC BY-NC-SA 4.0
豫ICP备2023001810号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式