はじめに
Go言語と言えば、並列プログラミングということでgoroutine、channelに触れてみようと思います。
goroutine
goroutine は go 文で関数を実行すると起動できます。
package main
import (
"log"
"time"
)
func f() {
log.Println("goroutine")
}
func main() {
go f()
log.Println("main")
time.Sleep(time.Second)
log.Println("finish")
}
実行結果
2018/01/23 20:35:46 main
2018/01/23 20:35:46 goroutine
2018/01/23 20:35:47 finish
go f()が実行されてから、すぐにlog.Println("main")が実行されていることが分かると思います。
こんなに簡単に並列処理ができるのはすごいですね...(まだ、使いこなせる気はしないですが)
goroutineは並列処理している関数が終了した時に終了されます。
channel
channel は goroutine 間での変数の受け渡しができます。
<- (オペレータ)を使って、値の受け渡しをするのですが、どういう型の値を扱うのか宣言できたりもします。
また、値の受け渡しがされてから、値が届くまでブロックしてくれるので、待ち時間を設ける必要がありません。
package main
import (
"fmt"
"log"
"time"
)
func f(c chan string) {
time.Sleep(10 * time.Second)
c <- "well come"
}
func main() {
c := make(chan string)
log.Print("started")
go f(c)
w1 := <-c//go f(c)実行後、即座に呼ばれていない
log.Print("finished")
fmt.Println(w1)
}
実行結果
2018/01/23 21:52:47 started
2018/01/23 21:52:57 finished
well come
go f(c)の実行後、w1 := <-cが即座に呼ばれていないことがわかると思います。
これらをより上手く使うために、selectといった機能があります。
select文
「select」文は、複数のチャネルを待機する場合に使用します。構文は「switch」文と同じく「case」を使用します。
「case」に指定したチャネルのうち、どれかを受信するまで待機します。
package main
import (
"fmt"
"log"
)
func printa(a chan string) {
a <- "a"
}
func printb(b chan string) {
b <- "b"
}
func printc(c chan string) {
c <- "c"
}
func main() {
a := make(chan string)
b := make(chan string)
c := make(chan string)
log.Print("started")
go printa(a)
go printb(b)
go printc(c)
for i := 0; i < 3; i++ {
select {
case msga := <-a:
fmt.Println("finished", msga)
case msgb := <-b:
fmt.Println("finished", msgb)
case msgc := <-c:
fmt.Println("finished", msgc)
}
}
log.Print("finished")
}
実行結果
2018/01/23 21:50:33 started
finished c
finished a
finished b
2018/01/23 21:50:33 finished