Go

【Go】チャネルの複数扱い

チャネルを複数扱った時の動作を確認してみたいと思います。

単純に複数使ってみる

単純にチャネルを複数使用したケースを書いてみます。
処理に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 

コメントを残す

メールアドレスが公開されることはありません。

© DeNnie.Lab All Rights Reserved.