Golang で 3 番目の Goroutine で 2 つの Goroutine の完了ステータスを追跡するためのベスト プラクティスは何ですか? Golang では、2 つのゴルーチンの完了ステータスを追跡し、その結果を 3 番目のゴルーチンで処理するには、同期パッケージの WaitGroup を使用するのがベスト プラクティスです。 WaitGroup を使用すると、メインの Goroutine で他の Goroutine の完了を待つことができます。まず、WaitGroup オブジェクトを作成し、メインの Goroutine で Add メソッドを呼び出して待機中の Goroutine の数を設定する必要があります。次に、各ゴルーチンの最後に Done メソッドが呼び出され、そのゴルーチンの完了を通知します。最後に、3 番目のゴルーチンで Wait メソッドが呼び出され、すべてのゴルーチンが完了するまで待機します。こうすることで、両方のゴルーチンの結果を安全に追跡して処理できます。これは、複数の Goroutine の完了ステータスを追跡するための Golang のベスト プラクティスです。
3つのゴルーチンを同時に実行しています。そのうちの 2 つは何らかの処理を実行し、結果を結果チャネルに送信します。 3 番目のゴルーチンは、結果チャネルを読み取ることによって結果を「カウント」します。 waitgroup を使用して 2 つの計算ゴルーチンが完了するのを待ってから、結果チャネルを反復処理して結果を集計することもできますが、これでは拡張できず、巨大なバッファ サイズでバッファリングされた結果チャネルを作成する必要があり、これは受け入れられません。プロダクションコードで。
処理の実行中に結果をカウントしたいのですが、すべての統計が完了する前にプログラムを終了したくありません。 Go でこれを達成するためのベスト プラクティスは何ですか?
これは私の現在の方法であり、うまく機能します。これは少し不格好に見えるので、より良い方法があるかどうか疑問に思っていますか?
package main import ( "fmt" "sync" ) type T struct{} func main() { var widgetInventory int = 1000 transactions := make(chan int, 100) salesDone := make(chan T) purchasesDone := make(chan T) var wg sync.WaitGroup fmt.Println("Starting inventory count = ", widgetInventory) go makeSales(transactions, salesDone) go newPurchases(transactions, purchasesDone) wg.Add(1) go func() { salesAreDone := false purchasesAreDone := false for { select { case transaction := <-transactions: widgetInventory += transaction case <-salesDone: salesAreDone = true case <-purchasesDone: purchasesAreDone = true default: if salesAreDone && purchasesAreDone { wg.Done() return } } } }() wg.Wait() fmt.Println("Ending inventory count = ", widgetInventory) } func makeSales(transactions chan int, salesDone chan T) { for i := 0; i < 3000; i++ { transactions <- -100 } salesDone <- struct{}{} } func newPurchases(transactions chan int, purchasesDone chan T) { for i := 0; i < 3000; i++ { transactions <- 100 } purchasesDone <- struct{}{} }
適切な定義に適合しません良い。ここにホット for
ループがあります:
読み取り元のチャネルがない限り、default
ケースが実行されます。これは、チャネルの仕組みが原因で頻繁に発生します。
コードのこのわずかに調整されたバージョンは、このループの「熱さ」を示しています。 正確な結果はさまざまであり、かなり高い値になる可能性があります。
リーリーデフォルトで何かがブロックされていない限り、チャネルから select
するときに default
の状況は望ましくありません。そうしないと、このような熱サイクルが発生します。
nil
可能なチャネルを選択に使用します 通常、選択では閉じたチャネルを特定し、チャネル変数を nil
に設定します。select
は nil# からは決して成功しません。 ## チャネルはコンテンツを読み取るため、これにより事実上選択が「無効」になります。
コードの修正バージョン : を考えてみましょう。 リーリー
コンシューマーに対するこれらの調整により、ホット ループはなくなり、データがチャネルから読み取られるまで常にブロックされます。salesDone と
purchasesDone の両方が「通知」されると、
close(transactions) になります。
transactions
を使い果たし、 がクローズされると、transactions を nil に設定します。
transactions が nil でない場合にループします。このコードでは、すべてのチャネルが
nil であることを意味します。
main とスコープを共有しません。それ以外の場合、
transactions を
nil に設定すると、ゴルーチン間で共有される変数に書き込まれます。ただし、この場合は、
transactions から最後に読み取ることが「わかっている」ため、それは問題ではありません。
トランザクション の生成を完了するまで待つ必要があります。次に、
transactions を排出します。チャネルが閉じられて排出されると、
main は合計が完了したことを認識します。
select は必要ありません。そして、
select が各 "ワーカー" にケースを持たせるのは、おそらくかなり洗練されていないため、複数のワーカーをハードコーディングし、"完了" チャネルを個別に処理する必要があります。
var resultswgsync.WaitGroup
之外,还为消费者添加一个。defer wg.Done()
defer resultswg.Done()
在遍历 transactions
之前:
go func() { defer resultswg.Done() for transaction := range transactions { widgetInventory += transaction } }()
wg.Wait() close(transactions) resultswg.Wait()
package main import ( "fmt" "sync" ) func main() { var widgetInventory int = 1000 transactions := make(chan int, 100) var wg, resultswg sync.WaitGroup fmt.Println("Starting inventory count = ", widgetInventory) wg.Add(2) go makeSales(transactions, &wg) go newPurchases(transactions, &wg) resultswg.Add(1) go func() { defer resultswg.Done() for transaction := range transactions { widgetInventory += transaction } }() wg.Wait() close(transactions) resultswg.Wait() fmt.Println("Ending inventory count = ", widgetInventory) } func makeSales(transactions chan int, wg *sync.WaitGroup) { defer wg.Done() for i := 0; i < 3000; i++ { transactions <- -100 } } func newPurchases(transactions chan int, wg *sync.WaitGroup) { defer wg.Done() for i := 0; i < 3000; i++ { transactions <- 100 } }
您可以在这里看到,在此模式中可以有任意数量的生产者;您只需为每个生产者添加 wg.Add(1)
即可。
当我不知道每个工作人员会返回多少结果时,我一直使用这种模式来并行化工作。我发现它很容易理解,并且比尝试 select
多个通道简单得多。事实上,我什至想说,如果您发现自己从多个渠道进行 select
ing,您应该退后一步,确保它对您来说确实有意义。我使用 select
的频率远远低于使用等待组的频率。
以上が3 番目のゴルーチン内の 2 つのゴルーチンの完了ステータスを追跡するための Golang のベスト プラクティスは何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。