ReadMeFirst
最初、Context周りを色々調べた結果を記事にしていたのですが、
前提で並列処理がイメージつかないとアレかなぁ
と思ったので、goの並列処理について簡単にパパッとまとめておきます。
Goの並列処理はカジュアルなので、世に記事が溢れてるのはご愛嬌で。
概念 (goroutine and channel)
Goの並行処理を構成する2つの要素について簡単に。
goroutine
Goのランタイムによって管理される軽量スレッド。
関数の呼び出しに go
をつけるだけで立ち上げることができる。
go Doanything(ctx)
みたいな感じ。
channel
ざっくりリファレンスから抜粋。
並列実行された関数間における値の送受信を提供する型です。
宣言はchannelで扱う値の型と共に行います。
送信・受信それぞれの専用とすることも可能です。
chan T // T型の送信・受信 を宣言
chan <- float64 // float64の送信専用
<- chan int // intの受信専用
chan bool
と <- chan bool
は別の型になりますので、ビルド時にエラーになる。
cannot use sig (type chan<- bool) as type chan bool
初期化はmakeで。第2引数はbufferの最大長です。
make(chan int,100)
専用のオペレーターを使って値の送受信ができる。
- 値の送信は
channel <- 値
- 値の受信は
<-channel
- len(channel)で現在格納されている数を計る事もできる(buffer指定しないと0のまま)
channelの値は格納した順に取り出されます。
buffer数値はdefaultは0ですが、>0
の値を設定した場合、
最大長以上の書き込みをBlock(待ち)する事を利用して同時実行数を
制限するような使い方もあります。
close(channel)でchannelを閉じる事ができます。
簡単な例
Sample
package main
import (
"fmt"
"time"
)
func main() {
const totalExecuteNum = 6 //合計実行数
const maxConcurrencyNum = 3 //同時実行数
sig := make(chan string, maxConcurrencyNum)
res := make(chan string, totalExecuteNum)
defer close(sig)
defer close(res)
fmt.Printf("start concurrency execute %s \n", time.Now())
for i := 0; i < totalExecuteNum; i++ {
go wait6Sec(sig, res, fmt.Sprintf("no%d", i))
}
for {
//全部が終わるまで待つ
if len(res) >= totalExecuteNum {
break
}
}
fmt.Printf("end concurrency execute %s \n", time.Now())
}
func wait6Sec(sig chan string, res chan string, name string) {
sig <- fmt.Sprintf("sig %s", name)
time.Sleep(6 * time.Second)
fmt.Printf("%s:end wait 6sec \n", name)
res <- fmt.Sprintf("sig %s", name)
<-sig
}
実行結果
go run main5.go
start concurrency execute 2017-04-19 18:54:43.276968001 +0900 JST
no2:end wait 6sec
no3:end wait 6sec
no1:end wait 6sec
no5:end wait 6sec
no4:end wait 6sec
no0:end wait 6sec
end concurrency execute 2017-04-19 18:54:55.279826923 +0900 JST
channelのbuffer数を利用して同時実行数を制限してみました。
(合計回数(6) / 同時実行数(3)) * 6 = 12秒程度で終わってますね :)
IMO
カジュアルに使えるので、積極的に使うべきではあるけど、
基本的に並列処理自体、直列の処理より頭のリソースを使うし、
複数人開発で無秩序に色んなところで使われると、goroutineがバカスカ立ち上がったり(queueingで対策すれば良いけど)など
注意するべき所もあります:P
適用部分がサービスにとって、とにかく速度が大事な部分かどうかと言う、
複雑さと速度改善は天秤にかけるべきかな、と。
補足
contextの記事書く前に補足記事的に書いたのですが、
実際には、mattnさんが紹介してるsync package
などもあるので、見てみると良いのかな〜と思います:)