本日はgoroutineについて勉強しましたのでここでアウトプットさせて頂きます!
##Goroutineとは?
Goroutineとはgoステートメントで関数を指定することで、並行実行されるものです!
まずは普通の関数を書いてみます。
package main
import (
"fmt"
"time"
)
func goroutine(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func hoge(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
goroutine("world")
hoge("hello")
}
実行結果
world
world
world
world
world
hello
hello
hello
hello
hello
これは、普通に上から関数が0.1秒ごとに実行されているだけですね。
これを並行実行するためには、関数名の前にgoを付けるだけで並行実行できます。
(同じなので省略)
func main() {
go goroutine("world")
hoge("hello")
}
実行結果
hello
world
world
hello
world
hello
hello
world
world
hello
並行実行しているので出力が混じった状態になります。
このように goステートメントを指定するだけで並行実行が簡単に実現できます。
ここで、timeの部分をコメントアウトしてみるとどうなるでしょうか?
package main
import (
"fmt"
)
func goroutine(s string) {
for i := 0; i < 5; i++ {
//time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func hoge(s string) {
for i := 0; i < 5; i++ {
//time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go goroutine("world")
hoge("hello")
}
実行結果
hello
hello
hello
hello
hello
timeの部分をコメントアウトすると、helloしか出力されなくなりました。
これは、並列処理でgoroutineスレッドが生成されても、先にmain関数の処理が終わってしまったため、
goroutine関数が実行されずに終わってしまったからです。
このようにgorutineの処理が終わらなくてもプログラムのコードは終了してしまうということを覚えておきましょう。
ではこれを避けるためにはどうすればよいか?
sync.WaitGroupというのを使います。
##sync.WaitGroup
package main
import (
"fmt"
"sync"
)
func goroutine(s string, wg *sync.WaitGroup) {
for i := 0; i < 5; i++ {
fmt.Println(s)
}
wg.Done()
}
func hoge(s string) {
for i := 0; i < 5; i++ {
fmt.Println(s)
}
}
func main() {
var wg sync.WaitGroup
wg.Add(1)
go goroutine("world", &wg)
hoge("hello")
wg.Wait()
}
このようにwg
という変数を宣言してwg.Add(1)
で一個の並列処理があるということを記述し、
goroutine関数へwg
のアドレスを引数で渡してあげます。
そして、wg.Wait()
と書いてあげることで、gorutine関数のwg.Done()
が呼ばれるまで処理が終わるのを待ってくれます。
このように書くことでgoroutine関数が実行されずに処理が終わってしまうことを回避することができます。
ちなみに、wg.Add(1)
でgoroutineの処理が終わるのを待っているので、 wg.Done()
をコメントアウトするとエラーになります。
wg.Add
したらwg.Done()
を呼び出して処理が完了したことを示す必要があります。
またgoroutine関数は以下のように書くこともできます。
func goroutine(s string, wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 5; i++ {
fmt.Println(s)
}
}
deferステートメントを使うとその関数の処理が終わった後にwg.Done()が実行されるので、このように書くこともできるのです。
##最後まで読んでいただきありがとうございます!
間違いや感想などありましたらコメントいただけると嬉しいです!!