11. 並行処理
Do not communicate by sharing memory; instead, share memory by communicating.
11.1 goroutine
Goでの並行処理モデルは goroutine と呼ばれています。
goroutineには次の特徴があります。
- goステートメントによって独立実行される関数です。
- ネイティブスレッドよりも小さなメモリ使用量です。
- ネイティブスレッドと同様に個別のコールスタックを持つ。
- 低コストで実行可能なため、数千から数十万のgoroutineの実行が可能です。
goroutineを使って並行処理をしてみましょう。
並行処理にしたい関数の前に go をつけるだけです。
go list.Sort() // list.Sort() が並行処理として実行されます。
並行処理している間にmain()が終了すると、goroutineの実行状態にかかわらずプロセスが終了します。
package main import ( "fmt" "time" ) func say(s string){ for i := 0; i < 5; i++ { time.Sleep(100 * time.Millisecond) fmt.Println(s) } } func main(){ // 並行実行となる go say("Gopher") // 通常実行 say("Hello") }
以下の用にGopherとHelloが表示されれば成功です。
Gopher Hello Hello Gopher Gopher Hello Hello Gopher Gopher Hello Edit Channel
11.2 channel
goroutineとのコミュニケーション手段としてchannelがあります。
channelの初期化はmakeで行います。
また、chan intとすることでint型のchannelを定義しています。
// Declaring and initializing. var c chan int c = make(chan int) // or c := make(chan int)
channelでの送信
c <- 1
channelでの受信
// "<-" はデータフローの向きを示します value = <- c
channelはバッファを持つものと持たないものがあります。
バッファを持たないchannelは同期的にコミュニケーションが行われます。
1度channelを送信すると、受信されるまで送信はブロックされます。
c := make(chan int, 0) // バッファなし(0がバッファ)
バッファを持つchannelはセマフォのように使用できます。
channelの送信は受信の有無にかかわらずバッファ数までブロックされずに送信できます。
1つ受信される毎に1つ送信できるようになります。
c := make(chan int, 100) // バッファあり(100がバッファ)
channelを用いたプログラム
package main import ( "fmt" ) func main() { c := make(chan int) done := make(chan bool) go func() { for i := 0; i < 10; i++ { // cチャネルに送信 c <- i } // doneチャネルに送信 done <- true }() fmt.Println("start") for { select { case v := <-c: // cチャネル受信 fmt.Println(v) case <-done: // doneチャネル受信 fmt.Println("done") return } } }
実行結果
start 0 1 2 3 4 5 6 7 8 9 done
goroutineとchannelによって並行処理を簡単に記述することができました。