ホームページ  >  記事  >  バックエンド開発  >  Go の遅延とは何ですか?使い方?

Go の遅延とは何ですか?使い方?

藏色散人
藏色散人転載
2021-09-01 15:52:432556ブラウズ

この記事は、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函数即将返回

実際、各コルーチンは 2 つのコール スタックを維持します。

  • 1 つは通常の関数呼び出しスタックです。このスタックでは、隣接する 2 つの呼び出しが呼び出し関係を持ちます。スタックに遅れて配置された呼び出しは、スタックに先に配置された呼び出しによって呼び出されます。このスタックにプッシュされた最も古い呼び出しは、対応するコルーチンの開始呼び出しです。
  • もう 1 つのスタックは、前述の遅延呼び出しスタックです。遅延呼び出しスタック内の 2 つの呼び出しの間に呼び出し関係はありません。

関数パラメータの評価を遅延する

  • 遅延関数呼び出しの場合、実際のパラメータは次の値になります。この関数が呼び出されたときに呼び出され、遅延呼び出しスタックにプッシュされたときに評価されます。
  • 匿名関数本体内の式は、関数が通常に呼び出されたか遅延して呼び出されたかに関係なく、関数の実行時に 1 つずつ評価されます。
    例 1:
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 Print(a) 遅延呼び出しスタックに追加されるとき、a の値は 5 なので、defer Print (a) 出力結果は 5
Example 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() {
                fmt.Println("b=", i)
            }()
        }
    }()}

output:

a= 2
a= 1
a= 0

b= 3
b= 3
b= 3

i になります。最初の匿名関数ループは、fmt.Println 関数の実行中に遅延呼び出しスタックにプッシュされます。 call のときに推定される値なので、出力結果は 2, 1, 0 になります。 2 番目の無名関数の i は、無名関数呼び出しの終了フェーズで推定される値 (この時点で i は 3 になっています) なので、出力結果はは: 3、3、3。
実際には、2 番目の無名関数呼び出しを少し変更することで、無名関数 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(i int) {
                fmt.Println("b=", i)
            }(i)
        }
    }()}

output:

a= 2
a= 1
a= 0

b= 2
b= 1
b= 0

パニックとリカバリ(遅延回復)

Go は例外のスローとキャッチをサポートしていませんが、戻り値を使用して明示的にエラーを返すことをお勧めします。ただし、Go は例外のスロー/キャッチと同様のメカニズムをサポートしています。このメカニズムは、パニック/リカバリ メカニズムと呼ばれます。

組み込み関数 panic を呼び出してパニックを生成し、現在のコルーチンがパニック状態になるようにします。

パニック状態に入るということは、現在の関数呼び出しの戻りを開始させるもう 1 つの方法です。関数呼び出しがパニックを生成すると、関数呼び出しはすぐに終了フェーズに入り、関数呼び出し内のスタックにプッシュされた遅延呼び出しは、プッシュされた順序とは逆の順序で実行されます。

遅延関数呼び出し内で組み込み関数 recover を呼び出すことで、現在のコルーチンのパニックを解消し、現在のコルーチンを通常の状態に戻すことができます。

パニック状態のコルーチンが終了するまでは、パニックは他のコルーチンに広がりません。コルーチンがパニック状態で終了すると、プログラム全体がクラッシュします。次の 2 つの例を見てください。

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

p 関数がパニックになります (除数が 0)。コルーチンにはパニック回復メカニズムがないため、プログラム全体がクラッシュします。
p 関数が配置されているコルーチンにパニック リカバリ (遅延リカバリ) が追加されている場合、プログラムは正常に終了できます。

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
程序正常退出~~~

Golang 関連の知識については、golangチュートリアル列をご覧ください! ##

以上がGo の遅延とは何ですか?使い方?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はlearnku.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。