チャネルを複数扱った時の動作を確認してみたいと思います。
単純に複数使ってみる
単純にチャネルを複数使用したケースを書いてみます。
処理に10秒かかるゴルーチンと処理に5秒かかるゴルーチンを用意し、それぞれに別々のチャネルを渡します。
処理に時間がかかるチャネルの読み込みを先に書く
掲題の通りですが、処理に10秒かかるチャネルBの読み込みとその表示を、処理に5秒かかるチャネルAよりも先に書いてみました。
package main
import (
"fmt"
"time"
)
func sampleGoRoutineA(channel chan<- string) {
fmt.Printf("【開始】 " + time.Now().Format(time.TimeOnly) + " sampleGoRoutineA \n")
time.Sleep(5 * time.Second)
fmt.Printf("【終了】 " + time.Now().Format(time.TimeOnly) + " sampleGoRoutineA \n")
channel <- "終了A"
close(channel)
}
func sampleGoRoutineB(channel chan<- string) {
fmt.Printf("【開始】 " + time.Now().Format(time.TimeOnly) + " sampleGoRoutineB \n")
time.Sleep(10 * time.Second)
fmt.Printf("【終了】 " + time.Now().Format(time.TimeOnly) + " sampleGoRoutineB \n")
channel <- "終了B"
close(channel)
}
func main() {
channelA := make(chan string)
channelB := make(chan string)
go sampleGoRoutineA(channelA)
go sampleGoRoutineB(channelB)
// あえてAより処理に時間がかるBの読み込みを先に書いてみる
b := <-channelB
fmt.Printf(b + ": " + time.Now().Format(time.TimeOnly) + "\n")
a := <-channelA
fmt.Printf(a + ": " + time.Now().Format(time.TimeOnly) + "\n")
}
処理に5秒かかるチャネルAが終了すると、Bの読み込み完了を無視して処理が進むと思いましたが、チャネルBが処理をブロックしていて、エラーなく正常に動きました。
【開始】 20:00:34 sampleGoRoutineB
【開始】 20:00:34 sampleGoRoutineA
【終了】 20:00:39 sampleGoRoutineA
【終了】 20:00:44 sampleGoRoutineB
終了B: 20:00:44
終了A: 20:00:44
処理が早く終わるチャネルの読み込みを先に書く
先ほどとは逆です。
package main
import (
"fmt"
"time"
)
func sampleGoRoutineA(channel chan<- string) {
fmt.Printf("【開始】 " + time.Now().Format(time.TimeOnly) + " sampleGoRoutineA \n")
time.Sleep(5 * time.Second)
fmt.Printf("【終了】 " + time.Now().Format(time.TimeOnly) + " sampleGoRoutineA \n")
channel <- "終了A"
close(channel)
}
func sampleGoRoutineB(channel chan<- string) {
fmt.Printf("【開始】 " + time.Now().Format(time.TimeOnly) + " sampleGoRoutineB \n")
time.Sleep(10 * time.Second)
fmt.Printf("【終了】 " + time.Now().Format(time.TimeOnly) + " sampleGoRoutineB \n")
channel <- "終了B"
close(channel)
}
func main() {
channelA := make(chan string)
channelB := make(chan string)
go sampleGoRoutineA(channelA)
go sampleGoRoutineB(channelB)
// あえて処理の早いAの読み込みを先に書いてみる
a := <-channelA
fmt.Printf(a + ": " + time.Now().Format(time.TimeOnly) + "\n")
b := <-channelB
fmt.Printf(b + ": " + time.Now().Format(time.TimeOnly) + "\n")
}
先ほどと似ていますが、処理に5秒かかるチャネルAが終了すると、チャネルAの表示は行われましたが、チャネルBが処理をブロックしていて、エラーなく正常に動きました。
【開始】 20:02:54 sampleGoRoutineA
【開始】 20:02:54 sampleGoRoutineB
【終了】 20:02:59 sampleGoRoutineA
終了A: 20:02:59
【終了】 20:03:04 sampleGoRoutineB
終了B: 20:03:04
クローズ判定する
for rangeを使用してクローズされた事を検知します。
package main
import (
"fmt"
"time"
)
func sampleGoRoutineA(channel chan<- string) {
fmt.Printf("【開始】 " + time.Now().Format(time.TimeOnly) + " sampleGoRoutineA \n")
time.Sleep(5 * time.Second)
fmt.Printf("【終了】 " + time.Now().Format(time.TimeOnly) + " sampleGoRoutineA \n")
channel <- "終了A \n"
close(channel)
}
func sampleGoRoutineB(channel chan<- string) {
fmt.Printf("【開始】 " + time.Now().Format(time.TimeOnly) + " sampleGoRoutineB \n")
time.Sleep(5 * time.Second)
fmt.Printf("【終了】 " + time.Now().Format(time.TimeOnly) + " sampleGoRoutineB \n")
channel <- "終了B \n"
close(channel)
}
func main() {
channelA := make(chan string)
channelB := make(chan string)
go sampleGoRoutineA(channelA)
go sampleGoRoutineB(channelB)
// あえてAより処理に時間がかるBの読み込みを先に書いてみる
for valB := range channelB {
fmt.Printf(valB)
}
for valA := range channelA {
fmt.Printf(valA)
}
}
正常に動きました。ただし先ほどと違い、”終了B” が表示されてから、”終了A”が表示されます。
これは、処理に10秒かかるチャネルBのfor rangeが処理をブロックしているからと推測できます。
【開始】 19:51:02 sampleGoRoutineB
【開始】 19:51:02 sampleGoRoutineA
【終了】 19:51:07 sampleGoRoutineA
【終了】 19:51:07 sampleGoRoutineB
終了B
終了A