- 追加された行はこの色です。
- 削除された行はこの色です。
* Goルーチン [#c391b625]
#mynavi()
#setlinebreak(on);
* 概要 [#wc24aaa3]
#html(<div class="pl10">)
Go言語ではゴルーチン(Goroutine)による並列処理が簡単に出来る。
以下、簡単なサンプル等を記載する。
#html(</div>)
* 目次 [#zcf847c3]
#contents
- 関連
-- [[Go言語]]
-- [[ExecutorServiceの最大プールサイズを考える(Java)]]
- 参考
-- https://golang.org/pkg/sync/
-- https://tour.golang.org/concurrency/1
* メイン処理は起動したゴルーチンの終了を待たない [#pcb668e1]
#html(<div class="pl10">)
メイン処理は起動した各ゴルーチンの終了を待たない。(他言語でスレッド起動した場合と同様)
例として、それぞれN秒間スリープをするN個の関数をgoルーチンで実行するサンプルを作成してみた。
※メイン処理のスリープ秒数は調整できるようにコマンドライン引数として取得する。
sample_goroutine1.go
#mycode2(){{
package main
import (
"flag"
"fmt"
"strconv"
"time"
)
func main(){
printLog("main START")
mainSleepSec := getSleepSec()
for i := 1; i <= 5; i++ {
go sub(i) // ゴルーチンの生成
}
time.Sleep(time.Second * mainSleepSec)
printLog("main END")
}
func getSleepSec() time.Duration {
flag.Parse()
args := flag.Args()
if len(args) > 0 {
num, _ := strconv.Atoi(args[0])
return time.Duration(num)
} else {
return time.Duration(6)
}
}
func sub(no int){
sleep := time.Duration(no)
printLog(fmt.Sprintf("sub(%d) START (sleep: %d)", no, sleep))
time.Sleep(time.Second * sleep)
printLog(fmt.Sprintf("sub(%d) END", no))
}
func printLog(msg string){
t := time.Now()
//fmt.Printf("%d-%02d-%02dT%02d:%02d:%02d.%06d %s\n",
fmt.Printf("%02d:%02d:%02d.%06d %s\n",
//t.Year(), t.Month(), t.Day(),
t.Hour(), t.Minute(), t.Second(),
t.Nanosecond() / 1000, msg)
}
}}
メイン処理側のスリープを5秒にした場合
#myterm2(){{
$ go run sample_goroutine1.go 6
18:46:26.007975 main START
18:46:26.008156 sub(3) START (sleep: 3)
18:46:26.008169 sub(4) START (sleep: 4)
18:46:26.008187 sub(5) START (sleep: 5)
18:46:26.008195 sub(2) START (sleep: 2)
18:46:26.008252 sub(1) START (sleep: 1)
18:46:27.010345 sub(1) END
18:46:28.011988 sub(2) END
18:46:29.008312 sub(3) END
18:46:30.013348 sub(4) END
18:46:31.012110 sub(5) END
18:46:32.011292 main END
}}
メイン処理側のスリープを2秒にした場合
#myterm2(){{
$ go run sample_goroutine1.go 3
18:47:00.325989 main START
18:47:00.326173 sub(4) START (sleep: 4)
18:47:00.326166 sub(3) START (sleep: 3)
18:47:00.326189 sub(5) START (sleep: 5)
18:47:00.326195 sub(2) START (sleep: 2)
18:47:00.326234 sub(1) START (sleep: 1)
18:47:01.330963 sub(1) END
18:47:02.331014 sub(2) END
18:47:03.330935 sub(3) END
18:47:03.330962 main END
}}
上記の結果から分かる通り、メイン処理は実行したゴルーチンの終了を待たない。
#html(</div>)
* ゴルーチンの終了を待ち合わせる [#lef50fef]
#html(<div class="pl10">)
メイン処理側でゴルーチンの終了を待ち合わせるには sync.WaitGroup を利用する。
※Java等で言うところの join。
sample_goroutine2.go
#mycode2(){{
package main
import (
"fmt"
"sync"
"time"
)
var wait = new(sync.WaitGroup) // 追加
func main(){
printLog("main START")
for i := 1; i <= 5; i++ {
wait.Add(1) // 追加
go sub(i)
}
wait.Wait() // 追加
printLog("main END")
}
func sub(no int){
sleep := time.Duration(no)
printLog(fmt.Sprintf("sub(%d) START (sleep: %d)", no, sleep))
time.Sleep(time.Second * sleep)
printLog(fmt.Sprintf("sub(%d) END", no))
wait.Done() // 追加
}
func printLog(msg string){
t := time.Now()
fmt.Printf("%02d:%02d:%02d.%06d %s\n",
t.Hour(), t.Minute(), t.Second(),
t.Nanosecond() / 1000, msg)
}
}}
実行結果
#myterm2(){{
$ go run sample_goroutine2.go
18:52:51.336324 main START
18:52:51.336491 sub(5) START (sleep: 5)
18:52:51.336517 sub(3) START (sleep: 3)
18:52:51.336534 sub(2) START (sleep: 2)
18:52:51.336549 sub(4) START (sleep: 4)
18:52:51.336589 sub(1) START (sleep: 1)
18:52:52.340077 sub(1) END
18:52:53.341676 sub(2) END
18:52:54.337123 sub(3) END
18:52:55.341728 sub(4) END
18:52:56.339499 sub(5) END
18:52:56.339582 main END
}}
#html(</div>)
* ゴルーチンと値をやり取りする [#l21c0a94]
#html(<div class="pl10">)
チャンネルを使用してゴルーチンと値のやり取りが出来る。
チャンネルは「変数 := make(chan データ型)」で生成する事ができる。
sample_goroutine3.go
#mycode2(){{
package main
import (
"fmt"
"sync"
"time"
)
var wait = new(sync.WaitGroup)
func main(){
printLog("main START")
var channels []chan string
for i := 1; i <= 5; i++ {
// チャンネル生成
ch := make(chan string)
channels = append(channels, ch)
wait.Add(1)
go sub(i, ch)
}
wait.Wait()
for i, ch := range channels {
// チャンネルから値を受信
message := <-ch
fmt.Printf("message[%d]: %s\n", i, message)
}
printLog("main END")
}
func sub(no int, ch chan string){
sleep := time.Duration(no)
printLog(fmt.Sprintf("sub(%d) START (sleep: %d)", no, sleep))
time.Sleep(time.Second * sleep)
printLog(fmt.Sprintf("sub(%d) END", no))
wait.Done()
// チャンネルに値を送信
ch <- fmt.Sprintf("sub(%d) finished!", no)
}
func printLog(msg string){
t := time.Now()
fmt.Printf("%02d:%02d:%02d.%06d %s\n",
t.Hour(), t.Minute(), t.Second(),
t.Nanosecond() / 1000, msg)
}
}}
実行結果
#myterm2(){{
$ go run sample_goroutine3.go
19:29:13.767172 main START
19:29:13.767313 sub(5) START (sleep: 5)
19:29:13.767344 sub(3) START (sleep: 3)
19:29:13.767354 sub(4) START (sleep: 4)
19:29:13.767358 sub(2) START (sleep: 2)
19:29:13.767414 sub(1) START (sleep: 1)
19:29:14.770515 sub(1) END
19:29:15.772479 sub(2) END
19:29:16.768126 sub(3) END
19:29:17.771446 sub(4) END
19:29:18.772457 sub(5) END
message[0]: sub(1) finished!
message[1]: sub(2) finished!
message[2]: sub(3) finished!
message[3]: sub(4) finished!
message[4]: sub(5) finished!
19:29:18.772529 main END
}}
#html(</div>)