Go のファンアウト パターンを簡単に見てみましょう。一般に、ファンアウトは多数のタスクを同時に実行するために使用されます。
たとえば、データ パイプラインがあり、個々のアイテムを処理したいとします。 go ルーチンとチャネルを使用して、受信した項目を分割し、個々の項目を処理できます (たとえば、dB を入力します)。
これは実装するのが簡単なパターンです。ただし、デッドロックを防ぐためにチャネルを管理する必要があります。
// produce is simulating our single input as a channel func produce(id int) chan int { ch := make(chan int) go func() { for i := 0; i < 10; i++ { ch <- rand.Intn(20) } fmt.Printf("producer %d done\n", id) close(ch) // this is important!!! }() return ch } func worker(id int, jobs chan int, wg *sync.WaitGroup) { for value := range jobs { odd := "even" if (value & 1) == 1 { odd = "odd" } fmt.Printf("worker: %d, got %d is %s\n", id, value, odd) } wg.Done() } func main() { inputCh := produce(1) numWorkers := 3 jobs := make(chan int) // split input into individual jobs go func() { for value := range inputCh { jobs <- value } close(jobs) // this is important!!! }() // fan-out var wg sync.WaitGroup for i := 0; i < numWorkers; i++ { wg.Add(1) go worker(i, jobs, &wg) } wg.Wait() fmt.Println("done") }
ここでの主な考え方は、固定数のワーカーによって操作される必要がある一連のデータがあるということです。
入力として、一連の乱数を作成し、それらをチャネルに配置します。私たち彼らは、労働者が自分たちの「仕事」を引き出してくる別のチャネルに彼らを移動させます。
この例では、入力をジョブ チャネルに移動することは厳密には必要ありません。ワーカーに入力チャネルからプルさせることも同様に簡単です。ここではわかりやすくするためにここに示しています。
次に、固定数のワーカーを goroutine として起動します。各ワーカーは、処理するデータがなくなるまでジョブ チャネルからプルし、データがなくなった時点で WaitGroup に処理が完了したことを通知します。
メインスレッドは WaitGroup を使用して、すべてのワーカーが完了するまで、つまりすべてのジョブが処理されるまでスレッドが完了しないようにします。
重要な点は、このパターンでは入力シーケンスの処理順序が保証されないということです。多くの状況ではこれで問題ないかもしれません。たとえば、入力シーケンスは独自のタイムスタンプを含むデータ レコードであり、目標はレコードを dB で保存することです。この場合のファンアウトは許容されます。
最後の注意点として、シーケンス内のすべてのデータが送信された後にチャンネルを閉じる際のコメントがいくつか表示されます。これは重要です。チャネルから取得する範囲演算子は、データがなくなるとスリープします。これを確認するには、デッドロック状態を引き起こす close() ステートメントを 1 回コメントアウトします。ゴルーチンとチャネルは非常に強力ですが、賢く使用する必要があります。
あなたなら何が違うでしょうか?この例をどのように改善できるでしょうか?以下にコメントを残してください。
ありがとうございます!
この投稿とこのシリーズのすべての投稿のコードはここにあります
以上がファンアウトパターンの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。