Cet article vous est présenté par la rubrique tutoriel go languageLe sujet porte sur l'apprentissage et l'utilisation de go defer. J'espère qu'il sera utile aux amis dans le besoin !
En Go, un appel de fonction peut suivre un mot-clé defer
pour former un appel de fonction différé. Lorsqu'un appel de fonction est retardé, il ne sera pas exécuté immédiatement. Il sera poussé dans une pile d'appels différés maintenue par la coroutine actuelle. Lorsqu'un appel de fonction (qui peut ou non être un appel différé) revient et entre dans sa phase de sortie, tous les appels différés qui ont été poussés dans cet appel de fonction seront exécutés dans l'ordre inverse de l'ordre dans lequel ils ont été poussés sur la pile. . Lorsque tous ces appels retardés sont exécutés, l’appel de fonction se termine réellement. Un exemple simple :
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
En fait, chaque coroutine maintient deux piles d'appels.
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) est ajouté à la pile d'appels différés, la valeur de a est 5, donc le résultat de sortie de defer Print(a) est 5 Exemple 2 :rrreeeoutput:rrreeei dans la première boucle de fonction anonyme est la valeur estimée lorsque l'appel de la fonction fmt.Println est poussé dans la pile d'appels retardés, donc le résultat de sortie est 2, 1, 0, Le i dans la deuxième fonction anonyme est la valeur estimée pendant la phase de sortie de l'appel de fonction anonyme (i est devenu 3 à ce moment), donc le résultat est : 3, 3, 3. En fait, avec une légère modification du deuxième appel de fonction anonyme, il peut afficher le même résultat que celui de la fonction anonyme :
rrreee
panic
pour générer une panique afin de mettre la coroutine actuelle dans un état de panique. 🎜🎜Entrer dans une condition de panique est une autre façon pour l'appel de fonction en cours de commencer à revenir. Une fois qu'un appel de fonction génère une panique, l'appel de fonction entrera immédiatement dans sa phase de sortie et les appels différés poussés sur la pile au sein de l'appel de fonction seront exécutés dans l'ordre inverse de l'ordre dans lequel ils ont été poussés. 🎜🎜En appelant la fonction intégrée recover
dans un appel de fonction retardé, une panique dans la coroutine actuelle peut être éliminée, permettant à la coroutine actuelle de rentrer dans des conditions normales. 🎜🎜Avant qu'une coroutine paniquée ne sorte, la panique ne se propagera pas aux autres coroutines. Si une coroutine se termine dans un état de panique, elle fera planter tout le programme. Regardez les deux exemples suivants : 🎜rrreee🎜output:🎜rrreee🎜p fonction panique (le diviseur est 0), car la coroutine n'a pas de mécanisme de récupération de panique, provoquant le crash de l'ensemble du programme. 🎜Si la coroutine où se trouve la fonction p est ajoutée avec une récupération de panique (différer + récupérer), le programme peut se terminer normalement. 🎜rrreee🎜sortie :🎜rrreee🎜🎜Pour plus de connaissances sur le golang, veuillez visiter la colonne 🎜🎜golang🎜🎜tutoriel 🎜 !Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!