Yuri’s Tech Note

技術系アウトプット

A Tour of Go vol.8 (Goroutines)

Goroutines

・軽量なスレッド
・goキーワードに続く関数を新しいスレッド(=goroutine)で実行する
・goroutineが実行されていてもmain()が終了するとプロセスが終了する点は注意
・Message-passing communication(各プロセスはメッセージを送り合い、内容は書き変わらない)

Channels

・goroutine 間でのメッセージパッシングをするためのもの
・メッセージの型を指定できる
・bufferで1度に扱えるメッセージの量を指定できる
・送信用チャネルはクローズする必要がある
・後述のselectで複数のチャネルから同時にメッセージを受信できる

// チャネルの生成
make(chan 要素数,キャパシティ)
make(chan 要素型)

// チャネルへの値の送受信
チャネル <- 送信する値
<- チャネル

// バッファの指定
ch := make(chan int, 100)
// 第二引数で送信先であるチャネル(変数:c)を受け取る
func sum(s []int, c chan int) {
    sum := 0
    for _, v := range s {
        sum += v
    }
    // send sum to c
    c <- sum
}

func main() {
    s := []int{7, 2, 8, -9, 4, 0}

    // int型のチャネルを生成
    c := make(chan int)

    go sum(s[:len(s)/2], c)
    go sum(s[len(s)/2:], c)

    // receive from c
    x, y := <-c, <-c

    fmt.Println(x, y, x+y)
}

Select

・複数ある case のいずれかが準備できるようになるまでブロックし、準備ができた case を実行
・複数の case の準備ができている場合、 case はランダムに選択される

func fibonacci(c, quit chan int) {
    x, y := 0, 1
    for {
        select {
            // 変数xの値をチャネルcを通して送信
            case c <- x:
                x, y = y, x+y
            // チャネルquitが準備できたら"quit"を出力
            case <-quit:
                fmt.Println("quit")
                return
            }
    }
}

func main() {
    // 1:チャネル作成
    c := make(chan int)
    quit := make(chan int)
    go func() {
        // 2:0~9まで10回反復処理
        for i := 0; i < 10; i++ {
            // 受信したメッセージを表示
            fmt.Println(<-c)
        }
        // 送信用チャネルquitにメッセージ0を送信
        quit <- 0
    }()
    // フィボナッチ関数にチャネルを引数として渡して実行
    fibonacci(c, quit)
}