Maison >développement back-end >Golang >Qu'est-ce que le report dans Go ? Comment l'utiliser ?

Qu'est-ce que le report dans Go ? Comment l'utiliser ?

藏色散人
藏色散人avant
2021-09-01 15:52:432574parcourir

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)}
output:

参数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函数参数估值

  • 对于一个延迟函数调用,它的实参是在此调用被推入延迟调用堆栈的时候被估值的。
  • 一个匿名函数体内的表达式是在此函数被执行的时候才会被逐个估值的,不管此函数是被普通调用还是延迟调用。
    例子1:
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来产生一个恐慌以使当前协程进入恐慌状况。

进入恐慌状况是另一种使当前函数调用开始返回的途径。 一旦一个函数调用产生一个恐慌,此函数调用将立即进入它的退出阶段,在此函数调用中被推入堆栈的延迟调用将按照它们被推入的顺序逆序执行。

通过在一个延迟函数调用之中调用内置函数recoverEn fait, chaque coroutine maintient deux piles d'appels.

  • L'une est la pile d'appels de fonction normale. Dans cette pile, deux appels adjacents ont une relation d'appel. Les appels placés tardivement sur la pile sont appelés par les appels placés tôt sur la pile. Le premier appel poussé dans cette pile est l'appel de démarrage de la coroutine correspondante.
  • Une autre pile est la pile d'appels différés mentionnée ci-dessus. Il n'existe aucune relation d'appel entre deux appels dans la pile d'appels différés.

évaluation des paramètres de fonction différée


  • Pour un appel de fonction différé, ses paramètres réels sont évalués lorsque l'appel est poussé dans la pile d'appels différés de.
  • Les expressions dans le corps d'une fonction anonyme seront évaluées une par une lors de l'exécution de la fonction, que la fonction soit appelée normalement ou retardée. Exemple 1 :
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

output:🎜rrreee🎜🎜🎜🎜Panique et récupération (différer + récupérer)🎜 🎜🎜Go ne prend pas en charge le lancement et la capture d'exceptions, mais il est recommandé d'utiliser des valeurs de retour pour renvoyer explicitement les erreurs. Cependant, Go prend en charge un mécanisme similaire au lancement/capture d’exception. Ce mécanisme est appelé mécanisme de panique/récupération. 🎜🎜Nous pouvons appeler la fonction intégrée 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!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer