defer延迟语句

简介: 快速学习defer延迟语句

1、什么是延迟语句

defer是go语言提供的一种注册延迟调用的机制,通常用来:打开/关闭连接,打开/关闭锁,打开/关闭文件等

defer + 、语句、
复制代码


2、defer语句的执行顺序

  • 同一个函数内的defer是“后进先出”的顺序,defer语句会被压入一个栈,当外层函数结束的时候,汇之星这个栈中的defer语句
func test(){
    fmt.Println(1)
    fmt.Println(2)
}
复制代码
  • 执行的结果是2,1


3、defer语句的变量必须是确定值才会被压入栈中

  • defer执行的时候,函数或者语句中的变量的值必须是确定的才会被压入栈中
func add(x, y int) int {
   c := x + y
   fmt.Println(c)
   return c
}
func testI3() {
   a := 1
   b := 2
   defer fmt.Println("a:", a)
   defer add(a, add(a, b))
   a = 3
   defer fmt.Println("a:", a)
}
复制代码
  • 这段代码得到的结果是3、 a: 3、 4、 a: 1
  • 这是执行第二个defer时候,会先确定add中另一个参数的值,所以会输出一个3,然后编程defer add(a,3)。
  • 这时候第二个defer被压入栈中,然后以后进先出的顺序执行三个defer


4、defer函数变量引用

  • defer函数变量引用有两种方式:参数和闭包
  • 参数分为值类型和引用类型
  • 值类型会进行一个拷贝
  • 引用类型会在函数结束时所确定的执行
  • 如果是闭包,会根据defer真正调用时的上下文环境确定
//闭包
for i := 0; i < 3; i++ {
   defer func() {
      fmt.Println(i)
   }()
}
//引用类型
var a = new(int)
*a = 1
defer func() {
   fmt.Println(*a)
}()
*a = 2
复制代码
  • 最终的结果是3 3 3  、 2


5、defer的执行时机,拆解defer

  • 在go语言中return并不是一个原子操作,会生成两个指令
  • 1、返回值 == xxx
  • 2、真正结束
  • 如果有defer语句,defer会在1和2之间执行

image.png

func main(){
    c1 := add(1,2)
    c2 := sub(1,2)
    fmt.Println(c1,c2)
}
func add(x, y int)(c int){
    defer func (){
      c = 10
    }()
    return x + y
}
func sub(x,y int)(c int){
    c = x - y
    defer func(){
        c--
    }()
    return 
}
复制代码
  • 结果是10 2


6、defer配合恢复语句

  • panic会停掉当前正在执行的程序,而不只是当前线程
  • 而遇到panic后,程序会先执行当前线程defer列表中的defer语句,这样我们就可以配合一些语句恢复线程-- recover
  • defer -- recover 捕捉panic的原则
  • panic必须在defer函数中调用,而不能多层嵌套,也不能平级
defer func(){
    recover()
}()
panic("now is err!")
复制代码


7、return和painc之后的defer不会被执行

  • return和panic之后的defer不会被注册,下面可以自己测试一下
//return之后
defer func(){
    fmt.Println("a")
}()
if true {
   fmt.Println("b")
   return
}
defer func(){
    fmt.Println("c")
}()
复制代码
//panic之后
panic("now is err")
defer func(){
   recover()
}()
复制代码
  • 事实上,return和panic之后所有语句都不会被注册


8、为什么无法从其他协程中恢复当前协程的panic

  • 每个goroutine都被设计成一个独立的单元,有自己单独的执行享的数据,如果要通信只能使用channel或者加读写锁。这也就意味着没有返回值他的数据与goroutine交互。
  • 当然也可以通过channel设计一个通信机制,但是这样不够灵活。而且有的panic无法恢复,所以一般写代码的时候要多思考、多测试
相关文章
|
2月前
|
Java C#
如何避免在C#循环中使用await
如何避免在C#循环中使用await
128 9
|
3月前
|
JavaScript 前端开发 Go
async 和 defer的作用与区别
async 和 defer的作用与区别
|
4月前
|
JavaScript 前端开发 中间件
异步编程中使用 async/await 是否必须包含 try 和 catch 语句以实现错误处理?
异步编程中使用 async/await 是否必须包含 try 和 catch 语句以实现错误处理?
|
4月前
|
设计模式 Java Go
Go - 使用 sync.WaitGroup 来实现并发操作
Go - 使用 sync.WaitGroup 来实现并发操作
48 2
|
Web App开发 移动开发 JavaScript
面试官:说一下script 标签中 defer(推迟) 和 async(异步) 的区别
面试官:说一下script 标签中 defer(推迟) 和 async(异步) 的区别
121 0
执行ALTER TABLE语句时如何避免长时间阻塞并发查询
执行ALTER TABLE语句时如何避免长时间阻塞并发查询
195 0
|
7月前
|
JSON 前端开发 算法
2715. 执行可取消的延迟函数
2715. 执行可取消的延迟函数
48 0
|
7月前
|
数据库连接 Go 开发者
避免defer陷阱:拆解延迟语句,掌握正确使用方法
避免defer陷阱:拆解延迟语句,掌握正确使用方法
|
JavaScript 前端开发
JS引擎的执行机制event loop
JS引擎的执行机制event loop
73 0