技術向上

プログラミングの学び、気になるテクノロジーやビジネストレンドを発信

fan-out、fan-inパターン【Go】

タスクを複数のgoroutineに分割して、順序通りに処理させたい場合に利用します。

fan-inは、複数の入力を1つのchannelにまとめて受信するパターンです。
fan-outは、複数の関数(goroutine)が、1つのchannelから値を読み取り送信するパターンです。
fan-outはCPUに作業を分配するため、Workersパターンとも呼ばれます。

次の例では、producerとmulti2とmulti4、main関数をchannelを通して接続し、
複数の処理を並行に、かつタスク分割して順々に実行しています。
処理が終わったchannelは、closeしないとrangeループから抜けられずに、
deadlockを引き起こすため、注意が必要です。

func producer(first chan<- int) {    // firstは送信専用
    defer close(first) // closeしないとrageループがdeadlockを引き起こす
    for i := 0; i < 10; i++ {
        first <- i
    }
}

func multi2(first <-chan int, second chan<- int) {    // firstは受信専用、secondは送信専用
    defer close(second) // closeしないとrageループがdeadlockを引き起こす
    for v := range first {
        second <- v * 2
    }
}

func multi4(second <-chan int, third chan<- int) {    // secondは受信専用、thirdは送信専用
    defer close(third) // closeしないとrageループがdeadlockを引き起こす
    for v := range second {
        third <- v * 4
    }
}

func main() {
    first := make(chan int)
    second := make(chan int)
    third := make(chan int)

    go producer(first)
    go multi2(first, second)
    go multi4(second, third)
    for v := range third {
        fmt.Println(v)
    }
}


上記コードのなかで、func定義のchannel引数に「<-」が記載されていますが、
これによって、対象のchannelを、送受信どちらかの専用とすることができます。
1行見ただけで、そのchannelが何のためのchannelなのか分かるようになり、
コードの保守性が向上します。

このパターンに基づいて、処理を分割して開発することができれば、
メンテナンスのしやすさや、コードのわかりやすさにもつながるかと思います。

ゴルーチン、チャネルを利用した並行パターン / fujimisakari blog

postd.cc

hori-ryota.com

ascii.jp