如何尽早 return 来自循环内 gouroutine 的错误?
How to return the error from the gouroutine inside a loop early?
我在一个循环中有一个 goroutine,我处理错误的方式是将它添加到一个通道中,在所有 goroutine 完成后,我检查是否有错误,然后我 return因此。
这个问题是我想 return 一旦我得到一个错误,这样我就不会花时间等待所有的 goroutines 完成,因为它是低效的。
我尝试添加 select
语句但它不起作用并且我无法在 goroutines 中添加 select
语句因为我想退出 for 循环和 try
也有功能。
我该怎么做?
代码如下:
package main
import (
"sync"
"runtime"
"fmt"
"errors"
)
func try() (bool, error) {
wg := new(sync.WaitGroup)
s := []int{0,1,2,3,4,5}
ec := make(chan error)
for i, val := range s {
/*
select {
case err, ok := <-ec:
if ok {
println("error 1", err.Error())
return false, err
}
default:
}
*/
wg.Add(1)
i := i
val := val
go func() {
err := func(i int, val int, wg *sync.WaitGroup) error {
defer wg.Done()
if i == 3 {
return errors.New("one error")
} else {
return nil
}
}(i, val, wg)
if err != nil {
ec <- err
return
}
}()
}
wg.Wait()
select {
case err, ok := <-ec:
if ok {
println("error 2", err.Error())
return false, err
}
default:
}
return true, nil
}
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
b, e := try()
if e != nil {
fmt.Println(e.Error(), b)
}
}
我发现 tomb 对此很有用。下面是一个精简的非工作示例,它显示了要点,没有处理循环中的变量封装之类的事情。它应该给你想法,但我很乐意澄清任何一点。
package main
import (
"fmt"
"gopkg.in/tomb.v2"
"sync"
)
func main() {
ts := tomb.Tomb{}
s := []int{0,1,2,3,4,5}
for i, v := range s {
ts.Go(func() error {
// do some work here or return an error, make sure to watch the dying chan, if it closes,
//then one of the other go-routines failed.
select {
case <- ts.Dying():
return nil
case err := <- waitingForWork():
if err != nil {
return err
}
return nil
}
})
}
// If an error appears here, one of the go-routines must have failed
err := ts.Wait()
if err != nil {
fmt.Println(err)
}
}
在 select
语句之前使用 wg.Wait()
,您实际上是在等待所有 goroutines return.
The issue with this is that I want to return an error as soon as I get it
我假设你的意思是一旦其中任何一个 return 出现错误就停止 运行 goroutines。
在这种情况下,您可以使用 context.Context
来管理取消,但更好的是 errgroup.Group
,它很好地结合了上下文功能和同步:
Package errgroup provides synchronization, error propagation, and Context cancelation for groups of goroutines working on subtasks of a common task.
特别是Group.Go
:
The first call to return a non-nil error cancels the group; its error will be returned by Wait.
import (
"sync"
"runtime"
"fmt"
"errors"
"golang.org/x/sync/errgroup"
)
func try() (bool, error) {
errg := new(errgroup.Group)
s := []int{0,1,2,3,4,5}
for i, val := range s {
i := i
val := val
errg.Go(func() error {
return func(i int, val int) error {
if i == 3 {
return errors.New("one error")
} else {
return nil
}
}(i, val)
})
}
if err := errg.Wait(); err != nil {
// handle error
}
return true, nil
}
我在一个循环中有一个 goroutine,我处理错误的方式是将它添加到一个通道中,在所有 goroutine 完成后,我检查是否有错误,然后我 return因此。
这个问题是我想 return 一旦我得到一个错误,这样我就不会花时间等待所有的 goroutines 完成,因为它是低效的。
我尝试添加 select
语句但它不起作用并且我无法在 goroutines 中添加 select
语句因为我想退出 for 循环和 try
也有功能。
我该怎么做?
代码如下:
package main
import (
"sync"
"runtime"
"fmt"
"errors"
)
func try() (bool, error) {
wg := new(sync.WaitGroup)
s := []int{0,1,2,3,4,5}
ec := make(chan error)
for i, val := range s {
/*
select {
case err, ok := <-ec:
if ok {
println("error 1", err.Error())
return false, err
}
default:
}
*/
wg.Add(1)
i := i
val := val
go func() {
err := func(i int, val int, wg *sync.WaitGroup) error {
defer wg.Done()
if i == 3 {
return errors.New("one error")
} else {
return nil
}
}(i, val, wg)
if err != nil {
ec <- err
return
}
}()
}
wg.Wait()
select {
case err, ok := <-ec:
if ok {
println("error 2", err.Error())
return false, err
}
default:
}
return true, nil
}
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
b, e := try()
if e != nil {
fmt.Println(e.Error(), b)
}
}
我发现 tomb 对此很有用。下面是一个精简的非工作示例,它显示了要点,没有处理循环中的变量封装之类的事情。它应该给你想法,但我很乐意澄清任何一点。
package main
import (
"fmt"
"gopkg.in/tomb.v2"
"sync"
)
func main() {
ts := tomb.Tomb{}
s := []int{0,1,2,3,4,5}
for i, v := range s {
ts.Go(func() error {
// do some work here or return an error, make sure to watch the dying chan, if it closes,
//then one of the other go-routines failed.
select {
case <- ts.Dying():
return nil
case err := <- waitingForWork():
if err != nil {
return err
}
return nil
}
})
}
// If an error appears here, one of the go-routines must have failed
err := ts.Wait()
if err != nil {
fmt.Println(err)
}
}
在 select
语句之前使用 wg.Wait()
,您实际上是在等待所有 goroutines return.
The issue with this is that I want to return an error as soon as I get it
我假设你的意思是一旦其中任何一个 return 出现错误就停止 运行 goroutines。
在这种情况下,您可以使用 context.Context
来管理取消,但更好的是 errgroup.Group
,它很好地结合了上下文功能和同步:
Package errgroup provides synchronization, error propagation, and Context cancelation for groups of goroutines working on subtasks of a common task.
特别是Group.Go
:
The first call to return a non-nil error cancels the group; its error will be returned by Wait.
import (
"sync"
"runtime"
"fmt"
"errors"
"golang.org/x/sync/errgroup"
)
func try() (bool, error) {
errg := new(errgroup.Group)
s := []int{0,1,2,3,4,5}
for i, val := range s {
i := i
val := val
errg.Go(func() error {
return func(i int, val int) error {
if i == 3 {
return errors.New("one error")
} else {
return nil
}
}(i, val)
})
}
if err := errg.Wait(); err != nil {
// handle error
}
return true, nil
}