Go言語のゴルーチン・チャネルの使い方メモ
はじめに
何回かGo言語で処理を作ったことがあったけど、Go言語で並行処理を一切書いたことがなかったので、勉強がてら簡単な処理を書いてみることにした。
参考サイト
環境
並行処理しない
とりあえず並行処理していないコードを書いた
package sample import ( "fmt" "log" "time" ) func NotUseGoRoutine() { log.Println("---------- Start Sample 01 ----------") process("first ", 10) process("second", 10) process("third ", 10) log.Println("---------- End Sample 01 ----------") } func process(name string, loopCount int) { commonLoopMessage(name, loopCount) } func commonLoopMessage(name string, loopCount int) { for count := 1; count <= loopCount; count++ { time.Sleep(time.Second / 10) fmt.Printf("name: %s, loopCount: %d\n", name, count) } }
出力
2021/05/28 06:49:38 ---------- Start Sample 01 ---------- name: first , loopCount: 1 name: first , loopCount: 2 name: first , loopCount: 3 name: first , loopCount: 4 name: first , loopCount: 5 name: first , loopCount: 6 name: first , loopCount: 7 name: first , loopCount: 8 name: first , loopCount: 9 name: first , loopCount: 10 name: second, loopCount: 1 name: second, loopCount: 2 name: second, loopCount: 3 name: second, loopCount: 4 name: second, loopCount: 5 name: second, loopCount: 6 name: second, loopCount: 7 name: second, loopCount: 8 name: second, loopCount: 9 name: second, loopCount: 10 name: third , loopCount: 1 name: third , loopCount: 2 name: third , loopCount: 3 name: third , loopCount: 4 name: third , loopCount: 5 name: third , loopCount: 6 name: third , loopCount: 7 name: third , loopCount: 8 name: third , loopCount: 9 name: third , loopCount: 10 2021/05/28 06:49:41 ---------- End Sample 01 ----------
並行処理する
さっきのコードを並行処理するように書き換える
並行処理が終わる前に関数を抜けないように、チャネルを使い、並行処理が終わるまでブロックする
参考サイトを見つつ修正
func UseGoRoutine01() { log.Println("---------- Start Sample 02 ----------") ch1 := make(chan bool) ch2 := make(chan bool) ch3 := make(chan bool) go processWithCh01(ch1, "first ", 10) go processWithCh01(ch2, "second", 10) go processWithCh01(ch3, "third ", 10) <- ch1 fmt.Println("Receive ch1 message") <- ch2 fmt.Println("Receive ch2 message") <- ch3 fmt.Println("Receive ch3 message") log.Println("---------- End Sample 02 ----------") } func processWithCh01(ch chan bool, name string, loopCount int) { commonLoopMessage(name, loopCount) ch <- true }
出力
2021/05/28 06:49:41 ---------- Start Sample 02 ---------- name: second, loopCount: 1 name: third , loopCount: 1 name: first , loopCount: 1 name: second, loopCount: 2 name: third , loopCount: 2 name: first , loopCount: 2 name: third , loopCount: 3 name: second, loopCount: 3 name: first , loopCount: 3 name: third , loopCount: 4 name: first , loopCount: 4 name: second, loopCount: 4 name: second, loopCount: 5 name: third , loopCount: 5 name: first , loopCount: 5 name: first , loopCount: 6 name: second, loopCount: 6 name: third , loopCount: 6 name: third , loopCount: 7 name: second, loopCount: 7 name: first , loopCount: 7 name: first , loopCount: 8 name: third , loopCount: 8 name: second, loopCount: 8 name: third , loopCount: 9 name: second, loopCount: 9 name: first , loopCount: 9 name: first , loopCount: 10 Receive ch1 message name: second, loopCount: 10 Receive ch2 message name: third , loopCount: 10 Receive ch3 message 2021/05/28 06:49:42 ---------- End Sample 02 ----------
ちょっと修正
チャネルは破棄できるらしい。(チャネルの戻り値を無視することではなく、チャネルをメモリから破棄的な意味)
チャネル破棄について調べると以下のサイトを発見
チャネルを使う場合、以下の注意点があるらしい(他にもあるだろうけど)
- 破棄したチャネルにイベントを送ると panic する
- チャネルを多重に破棄すると panic する
- 受信が先にいなくなるとブロックし続ける
また「処理が終了したこと」のみを通知する場合、チャネルに使用する型は「struct{}」で良いらしい
「struct{}」のサイズが「0」なので、通知のデータサイズが無いことを明示できる
func UseGoRoutine02() { log.Println("---------- Start Sample 03 ----------") ch1 := make(chan struct{}) ch2 := make(chan struct{}) ch3 := make(chan struct{}) go processWithCh02(ch1, "first ", 10) go processWithCh02(ch2, "second", 10) go processWithCh02(ch3, "third ", 10) <- ch1 fmt.Println("Receive ch1 message") <- ch2 fmt.Println("Receive ch2 message") <- ch3 fmt.Println("Receive ch3 message") log.Println("---------- End Sample 03 ----------") } func processWithCh02(ch chan struct{}, name string, loopCount int) { commonLoopMessage(name, loopCount) close(ch) }
出力
2021/05/28 06:49:42 ---------- Start Sample 03 ---------- name: second, loopCount: 1 name: first , loopCount: 1 name: third , loopCount: 1 name: second, loopCount: 2 name: first , loopCount: 2 name: third , loopCount: 2 name: third , loopCount: 3 name: second, loopCount: 3 name: first , loopCount: 3 name: first , loopCount: 4 name: third , loopCount: 4 name: second, loopCount: 4 name: second, loopCount: 5 name: first , loopCount: 5 name: third , loopCount: 5 name: third , loopCount: 6 name: second, loopCount: 6 name: first , loopCount: 6 name: first , loopCount: 7 name: third , loopCount: 7 name: second, loopCount: 7 name: second, loopCount: 8 name: first , loopCount: 8 name: third , loopCount: 8 name: third , loopCount: 9 name: first , loopCount: 9 name: second, loopCount: 9 name: third , loopCount: 10 name: second, loopCount: 10 name: first , loopCount: 10 Receive ch1 message Receive ch2 message Receive ch3 message 2021/05/28 06:49:43 ---------- End Sample 03 ----------