ホームページ  >  記事  >  バックエンド開発  >  Golang の WaitGroup トラップを分析して問題を解決する

Golang の WaitGroup トラップを分析して問題を解決する

藏色散人
藏色散人転載
2021-09-14 15:57:021972ブラウズ

この記事は、Golang の WaitGroup トラップに関する go 言語チュートリアル コラムで紹介されています。

sync.WaitGroup は、同時環境で非常に一般的に使用されるデータ構造であり、すべての処理を待機するために使用されます。コルーチンの末尾はコードを書く際のサンプルに従って記述されており、使い方を掘り下げる必要はありません。数日前、コルーチンで Add() 関数を実行できるかどうか疑問に思いましたが、答えは「ノー」です。

罠は、WaitGroup の 3 つの関数の呼び出しシーケンスにあります。まず、3 つの関数の機能を確認してみましょう。

  1. Add(delta int): デルタをカウンターに追加します。たとえば、コルーチンの開始時に 1 ずつ増加します。
  2. Done(): コルーチンが終了する前に実行され、カウンターが 1 減算されます。
  3. Wait(): ブロッキング待機カウンターは 0 です。

テストのテスト

次のプログラムは、親コルーチンを作成し、親コルーチンは 10 個のサブコルーチンを作成します。main 関数は、すべてのコルーチンが終了するのを待ってから終了します。以下のコードに問題があるかどうかを確認してください。

package main

import (
    "fmt"
    "sync"
)

func father(wg *sync.WaitGroup) {
    wg.Add(1)
    defer wg.Done()

    fmt.Printf("father\n")
    for i := 0; i < 10; i++ {
        go child(wg, i)
    }
}

func child(wg *sync.WaitGroup, id int) {
    wg.Add(1)
    defer wg.Done()

    fmt.Printf("child [%d]\n", id)
}

func main() {
    var wg sync.WaitGroup
    go father(&wg)

    wg.Wait()
    log.Printf("main: father and all chindren exit")
}

問題は見つかりましたか?次の実行結果が表示されない場合: サブコルーチンが終了する前にメイン関数が終了し始めます。

father
main: father and all chindren exit
child [9]
child [0]
child [4]
child [7]
child [8]

トラップ分析

上記の問題の理由は、Add() 関数がコルーチンの作成後にコルーチン内で実行され、この時点で Wait () 関数 は既に実行されているか、すべての Add() が実行される前に Wait() 関数が実行される可能性があります。 Wait( )実行すると、即座にWaitGroupのカウンタが0になり、Waitが終了してメインプログラムが終了するため、サブコルーチンが全て終了せずにメイン関数が終了します。

正しいアプローチ

Add 関数は、Wait 関数が実行される前に実行する必要があります。これは、Add 関数のドキュメントで指示されています。カウンタがゼロのときに発生する正のデルタは、Wait. の前に発生する必要があります。 Add 関数が Wait 関数の前に実行されるようにするにはどうすればよいですか?コルーチンの場合、コルーチン内のコードの実行時間が Wait 関数の実行時間より早いかどうかは予測できませんが、コルーチンを割り当てる前に Add 関数を実行し、その後 Wait 関数を実行することで確実に予測できます。関数。

以下は修正したプログラムと出力結果です。

リーリーリー

以上がGolang の WaitGroup トラップを分析して問題を解決するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

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