Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

第 33 期 Go defer 和逃逸分析 #199

Closed
yangwenmai opened this issue Mar 21, 2019 · 6 comments
Closed

第 33 期 Go defer 和逃逸分析 #199

yangwenmai opened this issue Mar 21, 2019 · 6 comments
Labels
Go 夜读 Go 夜读:主题分享 已分享 ✅ Go 夜读的分享状态:分享已完成。

Comments

@yangwenmai
Copy link
Member

https://reading.developerlearning.cn/reading/33-2019-03-07-defer-in-go/

Night Reading Go - Go source reading and offline technical discussion every Thursday night.

@wujunze
Copy link

wujunze commented Mar 22, 2019

有理有据 有套路 很不错

@XITEHIP
Copy link

XITEHIP commented Jul 25, 2019

defer4.go执行输出顺序是e1,e2,e3而不是e3,e2,e1。因为e1,e2,e3函数最后不管有没有return,编译器都会生成deferreturn()函数。这个deferreturn()里面就会执行jmpdefer从而执行deferd函数。而不是都压栈了统一执行。

@qcrao
Copy link
Member

qcrao commented Jul 25, 2019

defer4.go执行输出顺序是e1,e2,e3而不是e3,e2,e1。因为e1,e2,e3函数最后不管有没有return,编译器都会生成deferreturn()函数。这个deferreturn()里面就会执行jmpdefer从而执行deferd函数。而不是都压栈了统一执行。

你是对的,谢谢指正!

附上源码:

/*
1. 参数在defer注册的时候就会求值
2. 闭包引用的外部变量,使用的是err最后的值
*/
package main

import (
	"fmt"
	"errors"
)

func e1() {
	var err error

	defer fmt.Println(err, ": e1")

	err = errors.New("defer1 error")
	return
}

func e2() {
	var err error

	defer func() {
		fmt.Println(err)
	}()

	err = errors.New("defer2 error")
	return
}

func e3() {
	var err error

	defer func(err error) {
		fmt.Println(err, ": e3")
	}(err)

	err = errors.New("defer3 error")
	return
}

func main() {
	e1()
	e2()
	e3()
}

输出:

<nil> : e1
defer2 error
<nil> : e3

@alpha-baby
Copy link

defer6.go 中你本来的代码大概是这样的:

func main() {
	defer fmt.Println("defer main")
	var user = ""
	
	go func(){
		defer func() {
			fmt.Println("defer caller")
			if err := recover();err!=nil {
				fmt.Println("recover success. err:", err)
			}
		}()
		
		func(){
			defer func() {
				fmt.Println("defer here")
			}()
			
			if user == "" {
				panic("should set user env")
			}
// 剩下的不要了,没得什么用
		}()
		
	}()
	
	time.Sleep(time.Second) //时间我改了下
	fmt.Println("end of main function")
}

然后我把这个代码改成这样:

func main() {
	defer fmt.Println("defer main")
	var user = ""

	go func(){
		defer func() { // 这里增加了一层,
			func() {
				fmt.Println("defer caller")
				if err := recover();err!=nil {然后这个recover就不能捕获到下面的那个panic了
					fmt.Println("recover success. err:", err)
				}
			}()
		}()

		func(){
			defer func() {
				fmt.Println("defer here")
			}()

			if user == "" {
				panic("should set user env")
			}
		}()

	}()

	time.Sleep(time.Second)
	fmt.Println("end of main function")
}

然后,就不能捕获到panic了,这个东西怎么理解呀。原理是怎样的,能讲解下么。
@qcrao

@alpha-baby
Copy link

func main() {
	defer fmt.Println("defer main")
	var user = ""

	go func(){
		defer recover()

		func(){
			defer func() {
				fmt.Println("defer here")
			}()

			if user == "" {
				panic("should set user env")
			}
		}()

	}()

	time.Sleep(time.Second)
	fmt.Println("end of main function")
}

这样也是捕获不了的

@qcrao
Copy link
Member

qcrao commented Aug 30, 2019

func main() {
	defer fmt.Println("defer main")
	var user = ""

	go func(){
		defer recover()

		func(){
			defer func() {
				fmt.Println("defer here")
			}()

			if user == "" {
				panic("should set user env")
			}
		}()

	}()

	time.Sleep(time.Second)
	fmt.Println("end of main function")
}

这样也是捕获不了的

defer func() {
			fmt.Println("defer caller")
			if err := recover();err!=nil {
				fmt.Println("recover success. err:", err)
			}
}()

规定是这样,才能捕获。
至于你举的两个例子,可以转成汇编代码,自己研究下。
defer/recover 这个话题,煎鱼有篇文章写得很好,先研究下:https://book.eddycjy.com/golang/panic/panic-and-recover.html

@yangwenmai yangwenmai changed the title 第 33 期 Go 夜读之 Go defer 和逃逸分析 - Go 夜读 第 33 期 Go defer 和逃逸分析 Feb 2, 2020
@yangwenmai yangwenmai added Go 夜读 Go 夜读:主题分享 已分享 ✅ Go 夜读的分享状态:分享已完成。 labels Feb 2, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Go 夜读 Go 夜读:主题分享 已分享 ✅ Go 夜读的分享状态:分享已完成。
Projects
None yet
Development

No branches or pull requests

5 participants