遇到错误后,我想停止从频道阅读。我想我需要使用errroup.withcontext(),但是我不能完全围绕如何集成它。
这是输出:
Thread 2: STARTED
Thread 1: STARTED
Thread 0: STARTED
Thread 2: GOT=0
Thread 1: GOT=1
Thread 0: GOT=2
Thread 0: FAILED
Thread 1: GOT=3
Thread 2: GOT=4
Thread 2: GOT=5
Thread 1: GOT=6
Thread 1: GOT=7
Thread 2: GOT=8
panic: Thread 0: FAILED
package main
import (
"fmt"
"time"
"golang.org/x/sync/errgroup"
)
func main() {
const threads = 3
ch := make(chan int, threads)
eg := errgroup.Group{}
for i := 0; i < threads; i++ {
i := i
eg.Go(func() error {
fmt.Printf("Thread %d: STARTED\n", i)
for n := range ch {
fmt.Printf("Thread %d: GOT=%d\n", i, n)
time.Sleep(time.Duration(1) * time.Second)
// Simulate failed thread
if n == 2 {
fmt.Printf("Thread %d: FAILED\n", i)
return fmt.Errorf("Thread %d: FAILED", i)
}
}
return nil
})
}
for i := 0; i < 9; i++ {
ch <- i
}
close(ch)
if err := eg.Wait(); err != nil {
panic(err)
}
}
................................................................................. ............................................................
分析解答
errroup.WithContext()
可能是最好的方法(如您所知)。 这个答案值得一读;它提供了一个全面的解释(我不会尝试复制!)。
但是,由于这可能很难理解,这是使用您的代码(操场)的解决方案:
package main
import (
"context"
"fmt"
"time"
"golang.org/x/sync/errgroup"
)
func main() {
const threads = 3
ch := make(chan int, threads)
eg, ctx := errgroup.WithContext(context.Background())
for i := 0; i < threads; i++ {
i := i
eg.Go(func() error {
fmt.Printf("Thread %d: STARTED\n", i)
for n := range ch {
fmt.Printf("Thread %d: GOT=%d\n", i, n)
time.Sleep(time.Duration(1) * time.Second)
// Simulate failed thread
if n == 2 {
fmt.Printf("Thread %d: FAILED\n", i)
return fmt.Errorf("Thread %d: FAILED", i)
}
// For the purpose of this example we will just check the context after
// doing the work.
if ctx.Err() != nil {
fmt.Printf("Thread %d: Quiting due to context\n", i)
return fmt.Errorf("Thread %d: Context cancelled: ", i)
}
}
return nil
})
}
// We want to stop sending new work if there is a failure (otherwise `ch <- i` will block permanently);
sendLoop:
for i := 0; i < 9; i++ {
select {
case ch <- i:
case <-ctx.Done():
fmt.Printf("stopped\n")
break sendLoop // Just exit the loop if something goes wrong (you may want to do something else here)
}
}
close(ch) // Note: Important that this happens even if there is an error (otherwise `for n := range ch` may block, leaking goroutines)
fmt.Printf("all sent\n")
if err := eg.Wait(); err != nil {
panic(err)
}
}