この記事では、「デッドロック!」の背後にある原因を分析します。次のコード スニペットの Go チャネルに関係するデッドロック状況から発生するエラー:
package main import ( "fmt" "sync" ) func push(c chan int, wg sync.WaitGroup) { for i := 0; i < 5; i++ { c <- i } wg.Done() } func pull(c chan int, wg sync.WaitGroup) { for i := 0; i < 5; i++ { result, ok := <-c fmt.Println(result, ok) } wg.Done() } func main() { var wg sync.WaitGroup wg.Add(2) c := make(chan int) go push(c, wg) go pull(c, wg) wg.Wait() }
このコードを実行すると、次のエラーが発生します:
throw: all goroutines are asleep - deadlock!
このコードの sync.WaitGroup などの構造体が参照ではなく値で渡されるため、デッドロックが発生します。これは、WaitGroup を関数 (プッシュおよびプル) に渡すとき、実際には元のオブジェクトではなく WaitGroup のコピーを渡していることを意味します。
結果として、各関数は独自のコピーで動作することになります。 WaitGroup のコピーを作成し、wg.Done() を呼び出すと、自分自身のコピーがデクリメントされます。これにより、メインの goroutine が待機している元の WaitGroup が更新されないため、デッドロックが発生します。
この問題を解決するには、ポインタを渡す必要があります。値の代わりに WaitGroup に設定します。これにより、プッシュ関数とプル関数の両方が WaitGroup の同じインスタンス上で動作し、wg.Done() への呼び出しが元のオブジェクトに影響を与えるようになります。
コードの修正バージョンは次のとおりです。
package main import ( "fmt" "sync" ) func push(c chan int, wg *sync.WaitGroup) { for i := 0; i < 5; i++ { c <- i } wg.Done() } func pull(c chan int, wg *sync.WaitGroup) { for i := 0; i < 5; i++ { result, ok := <-c fmt.Println(result, ok) } wg.Done() } func main() { var wg sync.WaitGroup wg.Add(2) c := make(chan int) go push(c, &wg) go pull(c, &wg) wg.Wait() }
この変更を行うことで、WaitGroup へのポインターを関数に渡し、両方の関数が同じオブジェクト上で動作し、その状態を正しく更新できるようになります。これによりデッドロック状況が解消され、プログラムがエラーなく実行できるようになります。
以上がsync.WaitGroup 使用時の Go チャネルのデッドロック エラーを修正する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。