이 글은 go 언어튜토리얼 칼럼에서 소개한 글입니다. Go Defer를 배우고 활용하는 방법이 필요한 친구들에게 도움이 되었으면 좋겠습니다!
Go에서 함수 호출은 defer
키워드 뒤에 와서 지연된 함수 호출을 형성할 수 있습니다. 함수 호출이 지연되면 즉시 실행되지 않습니다. 현재 코루틴에 의해 유지 관리되는 지연된 호출 스택으로 푸시됩니다. 함수 호출(지연된 호출일 수도 있고 아닐 수도 있음)이 반환되어 종료 단계에 들어갈 때 이 함수 호출 내에서 푸시된 모든 지연된 호출은 스택에 푸시된 순서의 역순으로 실행됩니다. . 이러한 지연된 호출이 모두 실행되면 함수 호출이 실제로 종료됩니다. 간단한 예:
package mainimport "fmt"func sum(a, b int) { defer fmt.Println("sum函数即将返回") defer fmt.Println("sum函数finished") fmt.Printf("参数a=%v,参数b=%v,两数之和为%v\n", a, b, a+b)}func main() { sum(1, 2)}
参数a=1,参数b=2,两数之和为3 sum函数finished sum函数即将返回
defer
关键字后面,形成一个延迟函数调用。package mainimport "fmt"func Print(a int) {fmt.Println("defer函数中a的值=", a)}func main() {a := 10defer Print(a)a = 1000fmt.Println("a的值=", a)}
output:
a的值= 1000 defer函数中a的值= 10
事实上,每个协程维护着两个调用堆栈。
defer函数参数估值
package mainimport "fmt"func main() { func() { for i := 0; i < 3; i++ { defer fmt.Println("a=", i) } }() fmt.Println() func() { for i := 0; i < 3; i++ { defer func() { fmt.Println("b=", i) }() } }()}
output:
a= 2 a= 1 a= 0 b= 3 b= 3 b= 3
defer Print(a) 被加入到延迟调用堆栈的时候,a 的值是5,故defer Print(a) 输出的结果为5
例子2:
package mainimport "fmt"func main() { func() { for i := 0; i < 3; i++ { defer fmt.Println("a=", i) } }() fmt.Println() func() { for i := 0; i < 3; i++ { defer func(i int) { fmt.Println("b=", i) }(i) } }()}
output:
a= 2 a= 1 a= 0 b= 2 b= 1 b= 0
第一个匿名函数循环中的 i 是在 fmt.Println函数调用被推入延迟调用堆栈的时候估的值,因此输出结果是 2,1,0 , 第二个匿名函数中的 i 是匿名函数调用退出阶段估的值(此时 i 已经变成3了),故结果输出:3,3,3。
其实对第二个匿名函数调用略加修改,就能使它输出和匿名函数一相同的结果:
package mainimport ( "fmt" "time")func p(a, b int) int { return a / b}func main() { go func() { fmt.Println(p(1, 0)) }() time.Sleep(time.Second) fmt.Println("程序正常退出~~~")}
output:
panic: runtime error: integer pide by zero goroutine 6 [running]: main.p(...) /Users/didi/Desktop/golang/defer.go:9 main.main.func1() /Users/didi/Desktop/golang/defer.go:14 +0x12 created by main.main /Users/didi/Desktop/golang/defer.go:13 +0x39exit status 2
恐慌(panic)和恢复(defer + recover)
Go不支持异常抛出和捕获,而是推荐使用返回值显式返回错误。 不过,Go支持一套和异常抛出/捕获类似的机制。此机制称为恐慌/恢复(panic/recover)机制。
我们可以调用内置函数panic
来产生一个恐慌以使当前协程进入恐慌状况。
进入恐慌状况是另一种使当前函数调用开始返回的途径。 一旦一个函数调用产生一个恐慌,此函数调用将立即进入它的退出阶段,在此函数调用中被推入堆栈的延迟调用将按照它们被推入的顺序逆序执行。
通过在一个延迟函数调用之中调用内置函数recover
실제로 각 코루틴은 두 개의 호출 스택을 유지합니다.
package mainimport ( "fmt" "time")func p(a, b int) int { return a / b}func main() { go func() { defer func() { v := recover() if v != nil { fmt.Println("恐慌被恢复了:", v) } }() fmt.Println(p(1, 0)) }() time.Sleep(time.Second) fmt.Println("程序正常退出~~~")}
output:
恐慌被恢复了: runtime error: integer pide by zero 程序正常退出~~~
defer Print(a)가 지연된 호출 스택에 추가되고 a의 값은 5이므로 defer Print(a)의 출력 결과는 다음과 같습니다. 5 예제 2:rrreeeoutput:rrreeei 첫 번째 익명 함수 루프의 fmt.Println 함수 호출이 지연된 호출 스택에 푸시될 때 추정된 값이므로 출력 결과는 2, 1, 0, 두 번째 익명 함수의 i는 익명 함수 호출의 종료 단계에서 추정된 값(이때 i는 3이 됨)이므로 결과 출력은 3, 3, 3입니다. 사실 두 번째 익명 함수 호출을 약간 수정하면 익명 함수 호출과 동일한 결과를 출력할 수 있습니다.
rrreee
panic
을 호출하여 현재 코루틴을 패닉 상태로 만드는 패닉을 생성할 수 있습니다. 🎜🎜패닉 상태에 들어가는 것은 현재 함수 호출이 반환을 시작하는 또 다른 방법입니다. 함수 호출이 패닉을 생성하면 함수 호출은 즉시 종료 단계로 들어가고, 함수 호출 내의 스택에 푸시된 지연된 호출은 푸시된 순서의 역순으로 실행됩니다. 🎜🎜지연된 함수 호출에서 내장 함수 recover
를 호출하면 현재 코루틴의 패닉이 제거되어 현재 코루틴이 다시 정상 상태로 돌아갈 수 있습니다. 🎜🎜패닉 상태의 코루틴이 종료되기 전에 패닉 상태는 다른 코루틴으로 확산되지 않습니다. 코루틴이 패닉 상태에서 종료되면 전체 프로그램이 중단됩니다. 다음 두 가지 예를 살펴보세요. 🎜rrreee🎜output:🎜rrreee🎜p 함수 패닉(제수는 0). 코루틴에 패닉 복구 메커니즘이 없어 전체 프로그램이 충돌하기 때문입니다. 🎜p 함수가 위치한 코루틴에 패닉 복구(defer+recover) 기능을 추가하면 프로그램이 정상적으로 종료될 수 있습니다. 🎜rrreee🎜output:🎜rrreee🎜🎜더 많은 golang 관련 지식을 보려면 🎜🎜golang🎜🎜튜토리얼 칼럼을 방문하세요!위 내용은 Go에서 defer란 무엇인가요? 그것을 사용하는 방법?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!