Golang语言学习从入门到实战----defer

在这里插入图片描述

Hello,我是普通Gopher,00后男孩,极致的共享主义者,想要成为一个终身学习者。专注于做最通俗易懂的计算机基础知识类公众号。每天推送Golang技术干货,内容起于K8S而不止于K8S,涉及Docker、微服务、DevOps、数据库、虚拟化等云计算内容及SRE经验总结
=======================
初次见面,我为你准备了100G学习大礼包:
1、《百余本最新计算机电子图书》
2、《30G Golang学习视频》
3、《20G Java学习视频》
4、《90G Liunx高级学习视频》
5、《10G 算法(含蓝桥杯真题)学习视频》
6、《英语四级,周杰伦歌曲免费送!》
路过麻烦动动小手,点个关注,持续更新技术文章与资料!

defer

在函数中,程序员经常需要创建资源(比如:数据库连接、文件句柄、锁等),为了在函数执行完毕后,及时的释放资源,Go的设计者提供defer(延时机制)

简单的demo

package main

import "fmt"

func main() {
	res := sum(10, 20)
	fmt.Println("number4 res=", res)
}

func sum(n1 int, n2 int) int {
	// 函数执行完毕后,及时的释放资源
	// 先进后出
	defer fmt.Println("number1 n1 = ", n1)
	defer fmt.Println("number2 n2 = ", n2)

	res := n1 + n2
	fmt.Println("number3 res = ", res) // 最先执行
	return res
}

输出:

number3 res =  30
number2 n2 =  20
number1 n1 =  10
number4 res= 30
  • go执行到一个defer时,不会立即执行defer后的语句,而是将defer后的语句压入到一个"栈"(比喻)中, 然后继续执行函数下一个语句。

  • 当函数执行完毕后,在从defer栈中,依次从栈顶取出语句执行(注:遵守栈先入后出的机制)

  • 在defer将语句放入到栈时,也会将相关的值拷贝同时入栈

package main

import "fmt"

/*
输出:
number3 res =  30
number2 n2 =  20
number1 n1 =  10
number4 res= 30
*/
func main() {
	res := sum(10, 20)
	fmt.Println("number4 res=", res)
}

func sum(n1 int, n2 int) int {
	// 函数执行完毕后,及时的释放资源
	// 先进后出
	defer fmt.Println("number1 n1 = ", n1)
	defer fmt.Println("number2 n2 = ", n2)
	
	// 增加一段
	n1++
	n2++
	
	res := n1 + n2
	fmt.Println("number3 res = ", res) // 最先执行
	return res
}

输出:

number3 res =  32 // 输出结果为32
number2 n2 =  20 // 栈中的数值仍是存入前的数值
number1 n1 =  10
number4 res= 32

使用defer+recover来处理错误

package main

import (
	"fmt"
	"time"
)

func main() {

	test()
	for {
		fmt.Println("main()下面的代码...")
		time.Sleep(time.Second)
	}
}

func test() {
	defer func() {
		err := recover() // recover()内置函数,可以捕获到异常
		if err != nil {  // 捕获到异常
			fmt.Println("err=", err)
		}
	}()

	num1 := 10
	num2 := 0
	res := num1 / num2
	fmt.Println("res=", res)
}

输出:

err= runtime error: integer divide by zero
main()下面的代码...

defer在Go中的数据结构

type _defer struct {
	siz     int32
	started bool
	sp      uintptr
	pc      uintptr
	fn      *funcval
	_panic  *_panic
	link    *_defer
}

runtime._defer 结构体是延迟调用链表上的一个元素,所有的结构体都会通过 link字段串联成链表。

在这里插入图片描述

  • siz 是参数和结果的内存大小;
  • sp 和 pc 分别代表栈指针和调用方的程序计数器;
  • fn 是 defer 关键字中传入的函数;
  • _panic 是触发延迟调用的结构体,可能为空;

除了上述的这些字段之外,runtime._defer 中还包含一些垃圾回收机制使用的字段,这里为了减少理解的成本就都省去了

小结

defer关键字的实现主要依靠编译器和运行时的协作

编译期;

defer 关键字被转换runtime.deferproc
在调用defer 关键字的函数返回之前插入runtime.deferreturn;

运行时:

runtime.deferproc会将一个新的 runtime._defer结构体追加到当前Goroutine的链表头;
runtime.deferreturn会从 Goroutine的链表中取出runtime._defer 结构并依次执行;

  • 后调用的defer函数会先执行:
    • 后调用的defer函数会被追加到Goroutine _defer链表的最前面;
    • 运行runtime._defer时是从前到后依次执行;
  • 函数的参数会被预先计算;
    • 调用runtime.deferproc函数创建新的延迟调用时就会立刻拷贝函数的参数,函数的参数不会等到真正执行时计算;

更多关于Golang defer的编译过程与运行过程可以参考:

参考链接:https://draveness.me/golang/docs/part2-foundation/ch05-keyword/golang-defer/#533-

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值