我试图找出一个例程遇到错误时立即报告错误的基本模式。这是我正在使用的代码的基本逻辑
func doSomeWork(num int, errChan chan <- error) {
if num >= 10 {
errChan <- errors.New("Greater than 10")
}
return
}
const numWorkers int = 25
func run() error {
var wg sync.WaitGroup
errChan := make(chan error)
for i := 0; i < numWorkers; i++ {
wg.Add(1)
go func(num int) {
defer wg.Done()
doSomeWork(num, errChan)
}(i)
}
go func() {
wg.Wait()
close(errChan)
}()
err, ok := <-errChan
if !ok {
return nil
}
return err
}
这按预期工作,当 numWorkers < 10 and error when numworker is >= 10 时,它返回 nil。我实际上并不关心工人的数量,这只是一个简单的例子。然而,这感觉不太惯用,当一个例程失败而其他例程仍在运行并且等待所有例程的例程也仍在运行时,可能会出现一些例程泄漏。有没有一种更惯用的方法可以在一个例程失败时停止所有例程?
代码很接近,但是当发送多个错误时会泄漏 goroutine。下面的评论描述了修复程序。
const numWorkers int = 25
func run() error {
var wg sync.WaitGroup
// Create channel with capacity one to ensure that
// a goroutine can send to the channel before this
// goroutine is ready to receive.
errChan := make(chan error, 1) // <-- note 1 here
for i := 0; i < numWorkers; i++ {
wg.Add(1)
go func(num int) {
defer wg.Done()
doSomeWork(num, errChan)
}(i)
}
go func() {
wg.Wait()
// All goroutines are done, but the main goroutine
// will be waiting if there were no errors. Close
// the channel so that the main goroutine receives
// a zero error value.
close(errChan)
}()
// The if statement in the original is not needed because
// the zero value of the error interface is nil.
return <-errChan
}
func doSomeWork(num int, errChan chan<- error) {
if num >= 10 {
select {
case errChan <- errors.New("Greater than 10"):
// Successful send on channel
default:
// Channel is not ready. Do nothing because
// another goroutine previously sent an error.
}
}
}