golang concurrency - foolmacky/DevelopDocs GitHub Wiki
やや古いが自分が美しいと思うパターン
- https://jxck.hatenablog.com/entry/20130414/1365960707
- シンプルで無駄のないコードは常にすべてのスタートになると思う
- channelをクロージャーで参照している
- タイムアウトを実現するサンプル
func randomTime() time.Duration {
return time.Duration(rand.Intn(1e3)) * time.Millisecond
}
func worker(msg string) <-chan string {
receiver := make(chan string)
for i := 0; i < 300; i++ {
go func(i int) {
time.Sleep(randomTime())
msg := fmt.Sprintf("%d %s done", i, msg)
receiver <- msg
}(i)
}
return receiver
}
func main() {
receiver := worker("job")
for {
select {
case receive := <-receiver:
log.Println(receive)
case <-time.After(time.Second): // 一定時間経過したら受信
log.Println("timeout")
return // 受信時に終われば timeout 処理になる。
}
}
}
- 並列数を制御するサンプル
func worker(msg string) <-chan string {
limit := make(chan int, 5)
receiver := make(chan string)
go func() {
for i := 0; i < 100; i++ {
log.Println(runtime.NumGoroutine())
limit <- 1
go func(i int) {
msg := fmt.Sprintf("%d %s done", i, msg)
receiver <- msg
<-limit
}(i)
}
}()
return receiver
}
func main() {
receiver := worker("job")
for i := 0; i < 100; i++ {
log.Println(<-receiver)
}
}
##いつも混乱する点を少し
- contextでtimeoutしたり、キャンセルしたりするときは親routineでcontextを作成して、子routineに与えることで、子routineを制御する。作成した親自身を制御するのではない。 そして、制御した結果は親routineに返ってくる
- closureで記載した場合は、子routineに親routineの変数が見えるが、子routineがキャンセルされたことが通知されても、その瞬間は子routineは生きているかもしれないので、両者で共有している変数に書き込んだりすると RACEエラーが発生する可能性がある
- goroutine間のデータのやり取りは基本的にchannelを通じて行うべきだと思う。