私は単純な同時実行ユースケースをgoで持っています。どんな助けもありがとう。慣用的なゴルーチンの終了とエラー処理
リモートサーバーから不特定多数のリソースに並列にクエリを送信する方法fetchAll
を書きたいと思います。フェッチのいずれかが失敗した場合、その最初のエラーを直ちに返したいと思います。私は他のスレッドのクリーンアップに信号チャネルを作成することができhttps://blog.golang.org/pipelinesを読んでからhttps://play.golang.org/p/Be93J514R5
私が知っている:
package main
import (
"fmt"
"math/rand"
"sync"
"time"
)
func fetchAll() error {
wg := sync.WaitGroup{}
errs := make(chan error)
leaks := make(map[int]struct{})
defer fmt.Println("these goroutines leaked:", leaks)
// run all the http requests in parallel
for i := 0; i < 4; i++ {
leaks[i] = struct{}{}
wg.Add(1)
go func(i int) {
defer wg.Done()
defer delete(leaks, i)
// pretend this does an http request and returns an error
time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
errs <- fmt.Errorf("goroutine %d's error returned", i)
}(i)
}
// wait until all the fetches are done and close the error
// channel so the loop below terminates
go func() {
wg.Wait()
close(errs)
}()
// return the first error
for err := range errs {
if err != nil {
return err
}
}
return nil
}
func main() {
fmt.Println(fetchAll())
}
遊び場:
私の最初の、素朴な実装では、ゴルーチンをリーク。あるいは、私はおそらくそれを達成するためにcontext
を使用することができます。しかし、このようなシンプルなユースケースには、私が見逃している簡単な解決策があるはずです。
'ec:= chan error(nil)'は面白いです、前にこのパターンを見たことがありません。私は 'select'原因がランダムな順序で実行されたと考えました。'done <-true'が' ec <-err'の前に送信される競合状態はありますか? – gerad
良いキャッチは、絶対にレースです!私は急いで、私が言及したように、それを試していないと書いています(あなたはいつもそうするべきです)。幸いなことに、間違いを修正するだけで、コード全体がより簡単になります。この場合、 'chan error(nil)'トリックは必要ありません(select文からの送信をブロックして、複数の条件付き選択を書く必要がありません)。私の間違いを指摘してくれてありがとう:) – Aedolon
これはさらに簡素化することができます。個別の完了チャネルとエラーチャネルは必要ありません。改善する以外にもいくつかの点があります。 https://play.golang.org/p/1a0ZXuy3Dz –