defer 的执行顺序

77 阅读2分钟

1.只有defer

func Test1() {
 defer fmt.Println("1")
 defer fmt.Println("2")
}
//2
//1

2.defer+panic的时候

主程序语句+【go协程语句+协程defer+协程panic】

func test2() {
   defer beego.Info("in main")
   go func() {
      defer beego.Info("in goroutine")
      //主协程快太多,要是不等go协程,go协程里面连打印的代码都到不了,更不用说给机会运行到panic
      beego.Error("22222222222222222222")
      panic("恐慌打印...")
      //根据多次实验:
      //panic的执行在defer之后的,panic会让所有线程提前结束
   }()
   beego.Error("1111111111111111")
   time.Sleep(1 * time.Millisecond)//让min等go协程
}
//2023/05/25 11:37:51.610 [E]  1111111111111111 
//2023/05/25 11:37:51.610 [E]  22222222222222222222 
//2023/05/25 11:37:51.631 [I]  in goroutine 
//panic: 恐慌打印...

3.defer + panic嵌套

// 恐慌嵌套
func test() {
   defer beego.Info("in main")
   defer func() {
      beego.Info("in main2")
      panic("panic again")//panic语句和普通语句不一样,需要把所有panic单独看成一个panic栈【入栈】
   }()
   panic("panic again2")//【再入栈】
}
//in main2
//in main
//panic again2
//panic again

4.defer + panic + recover

只有写在defer里面的recover可以有效终止恐慌
因为写在里面的捕获代码先于panic执行【recover相当于预埋一个报警处理】

func test6() {
   defer func() {
      if err := recover(); err != nil {
         beego.Info("终止恐慌成功,程序退出仅仅表现为打印提示")
      }
   }()
   panic("恐慌发生")
}

5.defer + return

【return语句】【defer】【退出r】

func f1() (r int) {
   defer func() {
      r++ //匿名函数没有入参,那么就自动找外面有没有
   }()
   return 0
   //return 0 语句要习惯性看成两部分,【退出 r=0】
   //1.将r=0
   //2.因为没有新r的出现,所以r++操作的就是1
   //3.return r
}
//1
func f2() (r int) {//函数一开始指定的返回值接收是r,那么就只能是r
   t := 5
   defer func() {
      t = t + 5
   }()
   return t//很具有迷惑性,实际这里的t只是一个赋值作用
   //1.t=5
   //2.执行【return语句】r=t  所以r此时为5
   //3.t=5+5 所以t此时为10
   //4.return r
}
//5
func f3() (r int) {
	defer func(r int) {
		r = r + 5
	}(r)
	return 1

	//1.执行【return语句】r=1
	//2.1传给匿名函数的后面括号里
	//3.r=1+5 所以此时r为6【但是注意的是这个r是defer匿名函数重新定义成了自己的局部变量的】
	//   所以6只在defer里面有效,也就导致后面返回值返回r可定不是6
	//4.return r
}
//小范围可使用大范围变量值,反过来不行
//即:小用大,大不用小
//1