channel でデータをやり取りする

goroutine 同士でデータをやり取りするには channel を使います。channel は goroutine 間の通信路のようなもので、一方が送信したデータを他方が受信できます。

channel の作成

channel は make で作成します。型を指定する必要があります。

ch := make(chan int)      // int を送受信する channel
chStr := make(chan string) // string を送受信する channel

送信と受信

<- 演算子で送信と受信を行います。

ch <- 42    // channel に 42 を送信
v := <-ch   // channel から受信して v に代入

矢印の向きで送信か受信かが決まります。channel が左にあれば送信、右にあれば受信です。

実際に使ってみる

goroutine で計算した結果を channel で受け取る例です。

package main

import "fmt"

func double(n int, ch chan int) {
    ch <- n * 2
}

func main() {
    ch := make(chan int)

    go double(21, ch)

    result := <-ch
    fmt.Println(result) // 42
}

double 関数は引数を 2 倍して channel に送信します。main 関数は channel から結果を受信しています。

ここで重要なのは、<-ch の行で goroutine からの送信を待つという点です。channel の受信は、データが届くまでブロック(待機)します。これにより、time.Sleep を使わなくても goroutine の完了を待てます。

channel はブロックする

channel の送受信には、相手が必要です。

送信側

受信する goroutine がいないと、送信でブロックされる。

受信側

送信する goroutine がいないと、受信でブロックされる。

この仕組みのおかげで、goroutine 同士が自然に同期します。

複数の goroutine から結果を集める

複数の goroutine を起動して、それぞれの結果を集めることもできます。

package main

import "fmt"

func square(n int, ch chan int) {
    ch <- n * n
}

func main() {
    ch := make(chan int)

    for i := 1; i <= 3; i++ {
        go square(i, ch)
    }

    for i := 0; i < 3; i++ {
        fmt.Println(<-ch)
    }
}

3 つの goroutine がそれぞれ計算結果を送信し、main で 3 回受信しています。出力の順番は goroutine の実行順によって変わります。