我有一個簡單的併發用例在去,它讓我瘋狂我找不出一個優雅的解決方案。任何幫助,將不勝感激。慣用的goroutine終止和錯誤處理
我想寫一個方法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'原因是以隨機順序執行的。在'ec <-err'之前發送'done <-true'是否有競爭條件? – gerad
很好,絕對有一場比賽!我寫得很快,就像我提到的那樣,沒有測試它(你應該始終這樣做)。幸運的是,修復這個錯誤只會讓整個代碼變得更簡單,在這種情況下,不需要'chan error(nil)'技巧(當你想阻止select語句的發送時,這很有用,所以你不要不必寫多個條件選擇)。感謝您指出我的錯誤:) – Aedolon
這可以進一步簡化。你不需要單獨完成和錯誤的渠道,還有其他一些事情不會改進。 https://play.golang.org/p/1a0ZXuy3Dz –