技術向上

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

channelのselectパターン、for break【Go】

selectパターン

複数channelを同時並行に処理したい場合に利用します。
selectを用いることで、複数channelの状態を、ブロックすることなしに同時に監視できます。
値がchannelに入ったものから順に処理されます。

defaultが記載されていない場合で、かつどのchannelにも値が入っていない場合には、
selectスコープ自体をブロッキングするため、forループであろうと無駄なCPUリソースの消費がありません。
defaultが記載されている場合には、上記ブロッキングは発生しません。

func goroutine1(c chan int) {
    for {
        c <- 1
        time.Sleep(1 * time.Second)
    }
}

func goroutine2(c chan int) {
    for {
        c <- 2
        time.Sleep(2 * time.Second)
    }
}

func main() {
    c1 := make(chan int)
    c2 := make(chan int)

    go goroutine1(c1)
    go goroutine2(c2)

    t := time.NewTimer(5 * time.Second)    // 便宜上、タイマーを用意
    defer t.Stop()

    for {
        select {
        case <-t.C:    // タイマーの指定時間を超えたら終了させる
            close(c1)
            close(c2)
            return    // main関数による処理を終了する
        case msg1 := <-c1:    // <-を用いた、値の代入または破棄が必要
            fmt.Println(msg1)
        case msg2 := <-c2:    // <-を用いた、値の代入または破棄が必要
            fmt.Println(msg2)
        }
    }
}


for break

先述の例ですと、タイマーの指定時間を超えたらmain関数内の処理が終了してしまいますが、
forループに名前をつけて、breakするようにすれば、forループだけを抜けることができます。

func goroutine1(c chan int) {
    for {
        c <- 1
        time.Sleep(1 * time.Second)
    }
}

func goroutine2(c chan int) {
    for {
        c <- 2
        time.Sleep(2 * time.Second)
    }
}

func main() {
    c1 := make(chan int)
    c2 := make(chan int)

    go goroutine1(c1)
    go goroutine2(c2)

    t := time.NewTimer(5 * time.Second)
    defer t.Stop()

selectLoop:    // 任意の名前を付ける
    for {
        select {
        case <-t.C:
            close(c1)
            close(c2)
            break selectLoop    // 名前をつけたこのループをbreak
        case msg1 := <-c1:
            fmt.Println(msg1)
        case msg2 := <-c2:
            fmt.Println(msg2)
        }
    }
    fmt.Println("***********")    // selectLoopを抜けたら到達する
}


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

www.slideshare.net