idが詰まったsliceがある。(本当はidだけではないが話を簡単にするためにidのみとする)
そのsliceの要素1個1個に「なにかしらの処理」を加えつつ、処理結果を「1つのsliceに詰める」
これをx並列で行う。(とりあえずxの数は4とする)
処理中に起きてほしくないことが起きたときは、
1.最初に起きたエラーの内容を取る
2.すべての処理を即座に止める
2を完全に満たすのはこの要件である限り無理では…?と思いはしたので「ある程度はしょうがない」ものとした。してしまった。くるしい。
package main
import (
"errors"
"fmt"
"math/rand"
"strconv"
"sync"
"time"
)
var ids = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}
func main() {
rand.Seed(time.Now().UnixNano())
var wg sync.WaitGroup
var once sync.Once
// 並列数制御用
c := make(chan bool, 4)
// 失敗検知用
failC := make(chan struct{})
// 外部に伝えたい気持ち
ech := make(chan error, 1)
mt := &sync.Mutex{}
result := []string{}
for _, id := range ids {
wg.Add(1)
select {
case <-failC:
fmt.Println("fail break")
break
default:
// 渡してやらないとidの重複がでる場合がある...
go func(id int) {
c <- true
fmt.Println("start id:" + strconv.Itoa(id))
defer func() {
<-c
wg.Done()
}()
select {
case <-failC:
fmt.Println("fail goroutine")
return
default:
r := rand.Intn(10)
if r == 1 {
once.Do(func() {
fmt.Println("once id:" + strconv.Itoa(id))
ech <- errors.New("ERROR!!")
close(failC)
})
fmt.Println("rand hit id:" + strconv.Itoa(id))
return
}
mt.Lock()
result = append(result, "yes_"+strconv.Itoa(id))
mt.Unlock()
}
}(id)
}
}
wg.Wait()
if len(ech) > 0 {
err := <-ech
fmt.Println(err.Error())
}
fmt.Println(result)
fmt.Println("owari")
}
スマートさ云々の前に、一度も fail break
が出力されなかった。
件数が少ないからなのか…?
流石にすべてのgoroutineを生成し終わるまでfuncの中身を処理しない、ってことはあるまい。
うーん。。
errgroupパッケージはgo funcにid渡せなかったんですよね。
eg.Go(func(id int) error {
//しょり
}(id))
ってやったらid渡せなくて断念。
うーーーーーーん