ゴルーチンの同期

チャネルを使ってゴルーチンの同期をとることができる。
次のコードを見てほしい。

package main

import (
    "fmt"
    "time"
)

func makeRoutine(code string, in <-chan int) chan int {
    out := make(chan int)
    go func() {
        for {
            <- in fmt.Print(code)
            time.Sleep(200 * time.Millisecond) out <- 0
        }
    }()
    return out
}

func main() {
    ch1 := make(chan int)
    ch2 := makeRoutine("h", ch1)
    ch3 := makeRoutine("e", ch2)
    ch4 := makeRoutine("y", ch3)
    ch5 := makeRoutine("!", ch4)
    ch6 := makeRoutine(" ", ch5)
    for i := 0; i < 10; i++ {
        ch1 <- 0 <- ch6
    }
}

makeRoutine 関数は、文字列 code とチャネル in を引数にとる。まずチャネル out を作っておいて、無名関数をゴルーチンで起動、最後に out を返している。無名関数は無限ループになっていて、in から何か送信されてくるのを待って code を出力し、200 ミリ秒待ってから out にデータを送信する。
main 関数では、最初の(送信用)チャネル ch1 を作り、makeRoutine 関数に渡している。返ってきたチャネルは、次の makeRoutine 関数に渡され、返ってきたチャネルはさらに次の makeRoutine 関数に渡される。こうして5つの makeRoutine 関数の中のゴルーチンがチャネルを通じて数珠つなぎのようになる。これらのゴルーチンは、最初のチャネル ch1 にデータを送信することによって動作を開始し、最後のチャネル(main 関数から見ると ch6)にデータを送信して終わる。これを10回繰り返している。

実行してみよう。

^o^ > go run go_hey.go
hey! hey! hey! hey! hey! hey! hey! hey! hey! hey!

この実行例ではわからないけど、1文字ずつ時間を空けて hey! の文字列が10回出力されている。各ゴルーチンは1文字出力するだけなので、うまく同期して動作している様子がわかる。