Fan-OutとFan-In
処理を分散して収集します
「Fan-OutとFan-In」はCoddyKit上の無料Go Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはGo Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Go Academyコースには全4レッスンが含まれています。
ファンアウトとファンイン
ファンアウトとは、複数の goroutine を起動して1つのチャネルから読み取り、処理を分散することです。ファンインとは、複数の goroutine の出力を1つのチャネルにまとめることです。
- ファンアウトによってワーカー間に負荷を分散します
- ファンインによってコンシューマー向けに結果を集約します
ジェネレーターステージ
パイプラインはジェネレーターから始まります。ジェネレーターはチャネルを返す関数で、自身の goroutine からそのチャネルに値を送り、その後チャネルを閉じます。
func gen(nums ...int) <-chan int {
out := make(chan int)
go func() {
for _, n := range nums {
out <- n
}
close(out)
}()
return out
}ファンアウト
ファンアウトするには、同じ入力チャネルから読み取る複数の goroutine を起動します。それぞれが同じ処理関数を実行し、独自の出力チャネルを生成します。
in := gen(1, 2, 3, 4, 5)
c1 := square(in)
c2 := square(in)処理ステージ
square ステージは in から読み取り、各値を二乗して、新しい出力チャネルに送信します。このステージの複数のインスタンスで1つの入力を共有できます。
func square(in <-chan int) <-chan int {
out := make(chan int)
go func() {
for n := range in {
out <- n * n
}
close(out)
}()
return out
}merge によるファンイン
ファンインでは複数のチャネルを1つにまとめます。sync.WaitGroup でチャネルごとの goroutine を追跡し、終了用の goroutine はすべての goroutine が完了してから、統合した出力を閉じます。
func merge(cs ...<-chan int) <-chan int {
out := make(chan int)
var wg sync.WaitGroup
for _, c := range cs {
wg.Add(1)
go func(c <-chan int) {
for n := range c {
out <- n
}
wg.Done()
}(c)
}
go func() { wg.Wait(); close(out) }()
return out
}WaitGroup を使う理由
統合用の各 goroutine は、1つの入力チャネルから out へ値をコピーします。すべての入力を読み終えるまで、out を閉じてはいけません。WaitGroup はアクティブなコピー処理の数を数え、終了用の goroutine はそれらが完了するまで wg.Wait() でブロックします。
ループ変数のキャプチャ
古い Go では、各 goroutine が共有されたループ変数ではなく、それぞれのチャネルを束縛するように、クロージャーが c をパラメーターとして受け取る必要があります。c を明示的に渡すことで、典型的なループ変数のバグを回避できます。
完全なファンアウトとファンイン
このプログラムは数値を生成し、2つの square ステージにファンアウトしてから、ファンインして統合した結果を合計します。
package main
import (
"fmt"
"sync"
)
func gen(nums ...int) <-chan int {
out := make(chan int)
go func() { for _, n := range nums { out <- n }; close(out) }()
return out
}
func square(in <-chan int) <-chan int {
out := make(chan int)
go func() { for n := range in { out <- n * n }; close(out) }()
return out
}
func merge(cs ...<-chan int) <-chan int {
out := make(chan int)
var wg sync.WaitGroup
for _, c := range cs {
wg.Add(1)
go func(c <-chan int) { for n := range c { out <- n }; wg.Done() }(c)
}
go func() { wg.Wait(); close(out) }()
return out
}
func main() {
in := gen(1, 2, 3, 4, 5)
c1 := square(in)
c2 := square(in)
total := 0
for n := range merge(c1, c2) {
total += n
}
fmt.Println("sum of squares:", total)
}順序は保証されません
2つの square ステージが同じ入力を取り合うため、統合後の出力順序は非決定的です。順序が必要な場合は、各項目にインデックスを付けるか、ステージを1つにしてください。
ファンアウトするタイミング
あるステージがボトルネックで、処理を並列化できる場合にファンアウトします。上流のジェネレーターが遅い場合は、下流のワーカーを増やしても効果はありません。ワーカーが待機するだけになります。
バックプレッシャー
バッファーなしチャネルは自然なバックプレッシャーを生みます。遅いコンシューマーが処理しきれないと、速いステージはブロックします。これにより、パイプライン全体でメモリが際限なく増加するのを防ぎます。
クイックチェック
ファンインについての理解度を確認しましょう。
まとめ
ファンアウトとファンインについて学びました。
- ファンアウト:複数の goroutine で1つのチャネルから読み取ります
- ファンイン:WaitGroup を使って複数のチャネルを1つにまとめます
- 並列ステージ間では順序は維持されません
- バッファーなしチャネルはバックプレッシャーを提供します
よくある質問
「Fan-OutとFan-In」レッスンは無料ですか?
はい。「Fan-OutとFan-In」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Go Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Go Academyコースには全4レッスンが含まれています。
「Fan-OutとFan-In」で何を学びますか?
処理を分散して収集します ブラウザで直接実行するハンズオンコードでGo Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Go Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのGo Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。
「Fan-OutとFan-In」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このGo Academyレッスンでコードを書いて実行できますか?
はい。すべてのGo Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- ワーカープールパターン
- Fan-OutとFan-In
- パイプラインのステージ
- グレースフルシャットダウン