Dieser Artikel wird Ihnen durch die Go-SpracheTutorial-Kolumne vorgestellt. Das Thema dreht sich um das Erlernen und die Verwendung von Go Defer. Ich hoffe, dass es für Freunde in Not hilfreich sein wird.
In Go kann ein Funktionsaufruf einem Schlüsselwort defer
folgen, um einen verzögerten Funktionsaufruf zu bilden. Wenn ein Funktionsaufruf verzögert wird, wird er nicht sofort ausgeführt. Es wird in einen verzögerten Aufrufstapel verschoben, der von der aktuellen Coroutine verwaltet wird. Wenn ein Funktionsaufruf (der ein verzögerter Aufruf sein kann oder nicht) zurückkehrt und in seine Exit-Phase eintritt, werden alle verzögerten Aufrufe, die innerhalb dieses Funktionsaufrufs gepusht wurden, in umgekehrter Reihenfolge der Reihenfolge ausgeführt, in der sie auf den Stapel geschoben wurden . Wenn alle diese verzögerten Aufrufe ausgeführt werden, wird der Funktionsaufruf tatsächlich beendet. Ein einfaches Beispiel:
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
Tatsächlich verwaltet jede Coroutine zwei Aufrufstapel.
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("程序正常退出~~~")}
Ausgabe:
恐慌被恢复了: runtime error: integer pide by zero 程序正常退出~~~
defer Print(a) wird zum verzögerten Aufrufstapel hinzugefügt, der Wert von a ist 5, also ist das Ausgabeergebnis von defer Print(a). 5 Beispiel 2:rrreeeAusgabe:rrreeei in der ersten anonymen Funktionsschleife ist der geschätzte Wert, wenn der Funktionsaufruf fmt.Println in den verzögerten Aufrufstapel verschoben wird, sodass das Ausgabeergebnis 2, 1, 0 ist. i in der zweiten anonymen Funktion ist der Wert, der während der Exit-Phase des anonymen Funktionsaufrufs geschätzt wurde (i ist zu diesem Zeitpunkt 3 geworden), daher lautet die Ergebnisausgabe: 3, 3, 3. Tatsächlich kann er mit einer geringfügigen Änderung am zweiten anonymen Funktionsaufruf das gleiche Ergebnis wie der anonyme Funktionsaufruf ausgeben:
rrreee
panic
aufrufen, um eine Panik zu erzeugen und die aktuelle Coroutine in einen Panikzustand zu versetzen. 🎜🎜Das Eintreten in einen Panikzustand ist eine weitere Möglichkeit für den aktuellen Funktionsaufruf, zurückzukehren. Sobald ein Funktionsaufruf eine Panik auslöst, tritt der Funktionsaufruf sofort in seine Beendigungsphase ein und die zurückgestellten Aufrufe, die innerhalb des Funktionsaufrufs auf den Stapel verschoben werden, werden in umgekehrter Reihenfolge der Reihenfolge ausgeführt, in der sie übertragen wurden. 🎜🎜Durch den Aufruf der integrierten Funktion recover
in einem verzögerten Funktionsaufruf kann eine Panik in der aktuellen Coroutine beseitigt werden, sodass die aktuelle Coroutine wieder in den Normalzustand zurückkehren kann. 🎜🎜Bevor eine Coroutine in Panik aussteigt, breitet sich die Panik nicht auf andere Coroutinen aus. Wenn eine Coroutine in einem Panikzustand beendet wird, stürzt das gesamte Programm ab. Schauen Sie sich die folgenden zwei Beispiele an: 🎜rrreee🎜output:🎜rrreee🎜p-Funktion gerät in Panik (Divisor ist 0), da die Coroutine keinen Panik-Wiederherstellungsmechanismus hat, was zum Absturz des gesamten Programms führt. 🎜Wenn die Coroutine, in der sich die p-Funktion befindet, mit Panikwiederherstellung (Verzögern + Wiederherstellen) hinzugefügt wird, kann das Programm normal beendet werden. 🎜rrreee🎜Ausgabe:🎜rrreee🎜🎜Für mehr Golang-bezogenes Wissen besuchen Sie bitte die 🎜🎜Golang🎜🎜Tutorial-Kolumne 🎜Das obige ist der detaillierte Inhalt vonWas ist „Defer' in Go? Wie benutzt man es?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!