検索
ホームページバックエンド開発Golanggolang defer の実装原理を詳しく説明した記事

この記事は、go language チュートリアル コラムで golang defer の実装原理を紹介するもので、困っている友人の役に立てば幸いです。

defer は golang によって提供されるキーワードで、関数またはメソッドが実行を完了して戻った後に呼び出されます。
各 defer は defer 関数をスタックにプッシュします。関数またはメソッドが呼び出されると、実行のためにスタックから取り出されます。したがって、複数の defer の実行順序は最初から最後です。

for i := 0; i <p><strong>defer トリガーのタイミング</strong></p><p>公式 Web サイトでは、次のように明確に説明されています。<br>「defer」ステートメントは、実行が周囲のタイミングまで延期される関数を呼び出します。周囲の関数が return ステートメントを実行したか、その関数本体の最後に到達したか、または対応するゴルーチンがパニックしているため、関数は戻ります。</p><ol>
<li>defer ステートメントでラップされた関数が戻るとき</li> <li>defer ステートメントでラップされた関数が最後まで実行されたとき</li>
<li>
<p>現在の goroutine がパニックになったとき</p>
<pre class="brush:php;toolbar:false">    //输出结果:return前执行defer
   func f1() {
       defer fmt.Println("return前执行defer")
       return 
   }

   //输出结果:函数执行
   // 函数执行到最后
   func f2() {
       defer fmt.Println("函数执行到最后")
       fmt.Println("函数执行")
   }

   //输出结果:panic前  第一个defer在Panic发生时执行,第二个defer在Panic之后声明,不能执行到
   func f3() {
       defer fmt.Println("panic前")
       panic("panic中")
       defer fmt.Println("panic后")
   }

defer, return,戻り値の実行順序

まずは 3 つの例を見てみましょう

func f1() int { //匿名返回值
        var r int = 6
        defer func() {
                r *= 7
        }()
        return r
}

func f2() (r int) { //有名返回值
        defer func() {
                r *= 7
        }()
        return 6
}

func f3() (r int) { //有名返回值
    defer func(r int) {
        r *= 7
    }(r)
    return 6
}

f1 の実行結果は 6、f2 の実行結果は 42、f3 の実行結果は 6
golang の公式ドキュメントでは、return、defer、戻り値の実行順序が紹介されています。
周囲の関数が明示的な return ステートメントを通じて返される場合、遅延関数は、その return ステートメントによって結果パラメーターが設定された後に実行されます。ただし、関数が呼び出し元に戻る前に .

1. 最初に戻り値を割り当てます
2. defer ステートメントを実行します
3 . 関数 return を return

f1 の結果は 6 になります。 f1 は匿名戻り値です。匿名戻り値は return 実行時に宣言されます。したがって、defer が宣言されている場合、匿名戻り値にアクセスできません。defer を変更しても戻り値には影響しません。
f2 は最初に戻り値 r (r=6) を割り当て、defer ステートメントを実行し、defer が r (r = 42) を変更してから、関数が戻ります。
f3 は名前付き戻り値ですが、 r が defer のパラメータとして使用されているため、 defer を宣言するとパラメータがコピーされて渡されるため、 defer は defer 関数のローカル パラメータにのみ影響し、呼び出しには影響しません。関数の戻り値。

クロージャと無名関数
無名関数: 関数名のない関数。
クロージャ: 別の関数のスコープ内で変数を使用できる関数。

for i := 0; i <p><strong>defer ソース コードの分析</strong><br>defer の実装ソース コードは runtime.deferproc にあります<br>次に、関数が戻る前に runtime.deferreturn 関数を実行します。 <br>まず遅延構造を理解します。</p><pre class="brush:php;toolbar:false">    type _defer struct {
            siz     int32 
            started bool
            sp      uintptr // sp at time of defer
            pc      uintptr
            fn      *funcval
            _panic  *_panic // panic that is running defer
            link    *_defer
    }

sp と pc はそれぞれスタック ポインターと呼び出し元のプログラム カウンターを指します。fn は defer キーワードに渡される関数、Panic は遅延を引き起こすパニックです。実行されます。
defer キーワードが検出されるたびに、defer 関数は runtime.deferproc に変換されます。
deferproc は newdefer を通じて遅延関数を作成し、この新しい遅延関数を現在の goroutine の _defer リンク リストにハングします

    func deferproc(siz int32, fn *funcval) { // arguments of fn follow fn
            sp := getcallersp()
            argp := uintptr(unsafe.Pointer(&fn)) + unsafe.Sizeof(fn)
            callerpc := getcallerpc()

            d := newdefer(siz)
            if d._panic != nil {
                    throw("deferproc: d.panic != nil after newdefer")
            }
            d.fn = fn
            d.pc = callerpc
            d.sp = sp
            switch siz {
            case 0:
                    // Do nothing.
            case sys.PtrSize:
                    *(*uintptr)(deferArgs(d)) = *(*uintptr)(unsafe.Pointer(argp))
            default:
                    memmove(deferArgs(d), unsafe.Pointer(argp), uintptr(siz))
            }
            return0()
    }

newdefer は、まず sched と current p の deferpool から _defer 構造体を取り出します。deferpool に _defer がない場合は、新しい _defer が初期化されます。
_defer は現在の g に関連付けられているため、defer は現在の g に対してのみ有効です。
d.link = gp._defer
gp._defer = d //リンク リストを使用して、現在の g のすべての defer を接続します。

    func newdefer(siz int32) *_defer {
            var d *_defer
            sc := deferclass(uintptr(siz))
            gp := getg()
            if sc  0 {
                            d = pp.deferpool[sc][n-1]
                            pp.deferpool[sc][n-1] = nil
                            pp.deferpool[sc] = pp.deferpool[sc][:n-1]
                    }
            }
            ......
            d.siz = siz
            d.link = gp._defer
            gp._defer = d
            return d
    }

deferreturn 現在の g から _defer リンク リストを取り出し、それを実行すると、_defer を呼び出すたびに、freedefer が _defer 構造体を解放し、_defer 構造体を現在の p の deferpool に置きます。

defer パフォーマンス分析
defer は、開発においてリソースの解放やパニックのキャプチャなどに非常に役立ちます。一部の開発者は、遅延がプログラムのパフォーマンスに及ぼす影響を考慮しておらず、プログラム内で遅延を悪用している可能性があります。
パフォーマンス テストでは、遅延が依然としてパフォーマンスにある程度の影響を与えていることがわかります。 Yuchen の Go パフォーマンス最適化のヒント 4/1 には、defer ステートメントによって生じる追加のオーバーヘッドに関するテストがいくつかあります。

テスト コード

    var mu sync.Mutex
    func noDeferLock() {
        mu.Lock()
        mu.Unlock()
    }   

    func deferLock() {
        mu.Lock()
        defer mu.Unlock()
    }          
    
    func BenchmarkNoDefer(b *testing.B) {
        for i := 0; i <p><strong>テスト結果: </strong></p><pre class="brush:php;toolbar:false">    BenchmarkNoDefer-4      100000000               11.1 ns/op
    BenchmarkDefer-4        36367237                33.1 ns/op

前回のソース コード分析から、遅延が発生することがわかります。最初に deferproc を呼び出すと、パラメーターがコピーされ、 deferreturn も関連情報を抽出して実行が遅延します。これらはステートメントを直接呼び出すよりもコストがかかります。

遅延のパフォーマンスは高くありません。各遅延には 20ns かかります。関数内で複数回発生すると、パフォーマンスの消費は 20ns*n になります。CPU リソースの累積的な浪費は非常に大きくなります。

解決策: 例外キャプチャが必要な場合を除き、defer を使用する必要がありますが、その他のリソースリサイクルの延期については、失敗を判断した後に goto を使用してリソースリサイクルのコード領域にジャンプできます。競合リソースについては、使用後すぐにリソースを解放できるため、競合リソースを最適に使用できます。

golang 関連の知識の詳細については、golangチュートリアル コラムをご覧ください!

以上がgolang defer の実装原理を詳しく説明した記事の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明
この記事はsegmentfaultで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。
Golangの影響:速度、効率、シンプルさGolangの影響:速度、効率、シンプルさApr 14, 2025 am 12:11 AM

speed、効率、およびシンプル性をspeedsped.1)speed:gocompilesquilesquicklyandrunseffictient、理想的なlargeprojects.2)効率:等系dribribraryreducesexexternaldedenciess、開発効果を高める3)シンプルさ:

CとGolang:パフォーマンスが重要な場合CとGolang:パフォーマンスが重要な場合Apr 13, 2025 am 12:11 AM

Cは、ハードウェアリソースと高性能の最適化が必要なシナリオにより適していますが、Golangは迅速な開発と高い並行性処理が必要なシナリオにより適しています。 1.Cの利点は、ハードウェア特性と高い最適化機能に近いものにあります。これは、ゲーム開発などの高性能ニーズに適しています。 2.Golangの利点は、その簡潔な構文と自然な並行性サポートにあり、これは高い並行性サービス開発に適しています。

Golang in Action:実際の例とアプリケーションGolang in Action:実際の例とアプリケーションApr 12, 2025 am 12:11 AM

Golangは実際のアプリケーションに優れており、そのシンプルさ、効率性、並行性で知られています。 1)同時プログラミングはゴルチンとチャネルを通じて実装されます。2)柔軟なコードは、インターフェイスと多型を使用して記述されます。3)ネット/HTTPパッケージを使用したネットワークプログラミングを簡素化、4)効率的な同時クローラーを構築する、5)ツールと最高の実践を通じてデバッグと最適化。

Golang:Goプログラミング言語が説明しましたGolang:Goプログラミング言語が説明しましたApr 10, 2025 am 11:18 AM

GOのコア機能には、ガベージコレクション、静的リンク、並行性サポートが含まれます。 1. GO言語の並行性モデルは、GoroutineとChannelを通じて効率的な同時プログラミングを実現します。 2.インターフェイスと多型は、インターフェイスメソッドを介して実装されているため、異なるタイプを統一された方法で処理できます。 3.基本的な使用法は、関数定義と呼び出しの効率を示しています。 4。高度な使用法では、スライスは動的なサイズ変更の強力な機能を提供します。 5.人種条件などの一般的なエラーは、Getest Raceを通じて検出および解決できます。 6.パフォーマンス最適化Sync.Poolを通じてオブジェクトを再利用して、ゴミ収集圧力を軽減します。

Golangの目的:効率的でスケーラブルなシステムの構築Golangの目的:効率的でスケーラブルなシステムの構築Apr 09, 2025 pm 05:17 PM

GO言語は、効率的でスケーラブルなシステムの構築においてうまく機能します。その利点には次のものがあります。1。高性能:マシンコードにコンパイルされ、速度速度が速い。 2。同時プログラミング:ゴルチンとチャネルを介してマルチタスクを簡素化します。 3。シンプルさ:簡潔な構文、学習コストとメンテナンスコストの削減。 4。クロスプラットフォーム:クロスプラットフォームのコンパイル、簡単な展開をサポートします。

SQLソートのステートメントによる順序の結果がランダムに見えるのはなぜですか?SQLソートのステートメントによる順序の結果がランダムに見えるのはなぜですか?Apr 02, 2025 pm 05:24 PM

SQLクエリの結果の並べ替えについて混乱しています。 SQLを学習する過程で、しばしば混乱する問題に遭遇します。最近、著者は「Mick-SQL Basics」を読んでいます...

テクノロジースタックの収束は、テクノロジースタック選択のプロセスにすぎませんか?テクノロジースタックの収束は、テクノロジースタック選択のプロセスにすぎませんか?Apr 02, 2025 pm 05:21 PM

テクノロジースタックの収束とテクノロジーの選択の関係ソフトウェア開発におけるテクノロジーの選択、テクノロジースタックの選択と管理は非常に重要な問題です。最近、一部の読者が提案しています...

See all articles

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強力な PHP 統合開発環境

SublimeText3 Linux 新バージョン

SublimeText3 Linux 新バージョン

SublimeText3 Linux 最新バージョン

VSCode Windows 64 ビットのダウンロード

VSCode Windows 64 ビットのダウンロード

Microsoft によって発売された無料で強力な IDE エディター

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール