Go言語学習その30~Goroutineその1
Go における並行処理を実現する Goroutine を見てみます。
■ Goroutine で関数を並行実行
Go の処理は関数単位で動くわけですが
関数は Go のランタイムが管理するスレッドである
Goroutine 上で動きます。
Go の処理は必ず main 関数から始まりますが
この main 関数も Goroutine 上で動いていて、
main 関数から呼ばれる関数もデフォルトでは
main 関数と同じ Goroutine 上で動きます。
まず、以下のサンプルを実行してみます。
package main
import (
"fmt"
"time"
)
func sampleProcess() {
for i := 0; i < 5; i++ {
fmt.Println("★sampleProcess", i)
time.Sleep(10 * time.Millisecond)
}
}
func main() {
sampleProcess()
for i := 0; i < 5; i++ {
fmt.Println("★main", i)
time.Sleep(20 * time.Millisecond)
}
}
実行結果は以下になります。
main 関数から sampleProcess 関数が実行され
sampleProcess 関数の処理が終わってから
main 関数の処理が実行されてますね。
main 関数が実行されている Goroutine 上で
sampleProcess 関数も動いています。
では、sampleProcess 関数を
main 関数とは別の Goroutine 上で動かすにはどうすればいいでしょう?
以下のように関数呼び出し時に関数名の前に「go」を記載すれば
別の Goroutine 上で関数が実行されるようになります。
以下にサンプルを示します。
package main
import (
"fmt"
"time"
)
func sampleProcess() {
for i := 0; i < 5; i++ {
fmt.Println("★sampleProcess", i)
time.Sleep(10 * time.Millisecond)
}
}
func main() {
go sampleProcess()
for i := 0; i < 5; i++ {
fmt.Println("★main", i)
time.Sleep(20 * time.Millisecond)
}
}
前述のサンプルとの違いは
sampleProcess 関数呼び出しの行の先頭に
「go」を付けているところですね。
実行結果は以下になります。
上記結果は、実行する度に出力する順番が変わりますが
結果を見てみると
sampleProcess 関数の処理の終了を待たずに main 関数の処理が動いている
ことがわかります。
関数呼び出し時に「go」を付けることで
go sampleProcess()
main 関数は「自分が動いているものとは別の Goroutine」に
sampleProcess 関数の実行を依頼し、
依頼が終わったら sampleProcess 関数の終了を待たずに
すぐに自分の処理に戻ります。
main 関数と sampleProcess 関数 の処理が並行実行されるわけです。
※ time.Sleepをなぜしてる?
サンプルの for 文内で
time.Sleep(10 * time.Millisecond)
として処理を少し待機させてますが
これは
main 関数の処理が待機してるときに
sampleProcess 関数の処理結果が表示されたり
またその逆で
sampleProcess 関数の処理が待機してるときに
main 関数の処理結果が表示されたり
するようにすることで
並行実行されてるのを確認しやすくするためです。
■ main の Goroutine が終わったら全てが終わる
前述のサンプルから「time.Sleep~」を削除した
以下のサンプルを実行してみます。
package main
import (
"fmt"
)
func sampleProcess() {
for i := 0; i < 5; i++ {
fmt.Println("★sampleProcess", i)
}
}
func main() {
go sampleProcess()
for i := 0; i < 5; i++ {
fmt.Println("★main", i)
}
}
実行結果は以下になります。
この結果も実行する度に出力内容が変わる可能性がありますが
結果は以下のようになっています。
・main 関数の処理は for ループ5回分全て出力されている
・sampleProcess 関数の処理は for ループ回数が3回で止まってる
main 関数は
go sampleProcess()
で別の Goroutine で sampleProcess が実行されるように依頼した後
すぐに自分の処理に入り、
for ループ5回分の処理を実行します。
time.Sleep による待機が無いため実行はすぐに終わります。
sampleProcess 関数は
別の Goroutine 生成 ⇒ 関数処理開始
といった感じで「Goroutine 生成」が入る分
main 関数よりも開始が遅れるのですが
「for ループ回数が3回で止まってる」
のは
「main 関数の Goroutine が終わったため」
になります。
main 関数の Goroutine が終了してしまったら
main 関数から別の Goroutine で起動された関数の処理は
強制終了してしまうんですね。
なので、別の Goroutine で起動された関数の処理が終了するまで
main 関数の処理を何らかの方法で待機する必要がありますが、
これについては次回の記事で見てみようと思います。
※ main 関数の time.Sleep の待機時間を長くしてる理由
前述「■ Goroutine で関数を並行実行」でのサンプルでは
sampleProcess 関数側の time.Sleep は
time.Sleep(10 * time.Millisecond)
main 関数側の time.Sleep は
time.Sleep(20 * time.Millisecond)
と、main 関数側の time.Sleep の待機時間を少し長くしてますが
これは main 関数が先に終わらないようにするためです。
次回も Goroutine の話になります。
この記事が気に入ったらサポートをしてみませんか?