外部関数で sync.WaitGroup を利用するためのベスト プラクティス
Go で同時実行性に対処する場合、sync.WaitGroup を効果的に利用することが重要です。この記事では、待機グループを引数として外部関数に渡すときに発生する一般的な問題について説明します。
問題:
次のコードを考えてみましょう:
<code class="go">package main import ( "fmt" "sync" ) func main() { ch := make(chan int) var wg sync.WaitGroup wg.Add(2) go Print(ch, wg) // go func(){ for i := 1; i <= 11; i++ { ch <- i } close(ch) defer wg.Done() }() wg.Wait() //deadlock here } // Print prints all numbers sent on the channel. // The function returns when the channel is closed. func Print(ch <-chan int, wg sync.WaitGroup) { for n := range ch { // reads from channel until it's closed fmt.Println(n) } defer wg.Done() }</code>
このコードでは、指定された行でデッドロックが発生し、プログラムは 11 までではなく 1 から 10 までしか印刷しません。このエラーは、sync.WaitGroup のコピーを Print メソッドに渡すことで発生し、これが妨げになります。 Done メソッドへの予期された呼び出しです。
解決策 1:
この問題を解決するには、代わりに待機グループへのポインタを渡します:
<code class="go">package main import ( "fmt" "sync" ) func main() { ch := make(chan int) var wg sync.WaitGroup wg.Add(2) go Print(ch, &wg) go func() { for i := 1; i <= 11; i++ { ch <- i } close(ch) defer wg.Done() }() wg.Wait() //deadlock here } func Print(ch <-chan int, wg *sync.WaitGroup) { for n := range ch { // reads from channel until it's closed fmt.Println(n) } defer wg.Done() }</code>
wg のアドレスを渡すと、Print メソッドが main 関数で待機している待機グループの Done メソッドを呼び出すようになります。
解決策 2: 簡素化された Print メソッド
また、待機操作の知識が必要ないため、Print メソッドは WaitGroup 引数を削除することで簡素化できます。
<code class="go">package main import ( "fmt" ) func main() { ch := make(chan int) go func() { for i := 1; i <= 11; i++ { ch <- i } close(ch) }() for n := range ch { // reads from channel until it's closed fmt.Println(n) } } </code>
このシナリオでは、メインのゴルーチンがチャネルを直接受信して出力します。待機グループを含まないその値。このアプローチにより、必要な機能が維持され、Print メソッド内での WaitGroup 管理の必要がなくなります。
結論:
sync.WaitGroup を引数として外部関数に渡す場合、関数が待機中の待機グループへの正しい参照を確実に受け取ることが重要です。待機グループを直接処理するように関数をリファクタリングするか、待機グループへのポインタを渡すと、デッドロック エラーを効果的に防止し、適切な同時実行制御を確保できます。
以上がGo の外部関数で sync.WaitGroup を使用するときにデッドロックを回避するにはどうすればよいですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。