一旦遇到错误,我想停止从通道读取数据.我认为我需要使用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()可能是最好的方法(正如你所猜的那样).This answer值得一读;它提供了一个全面的解释(我不打算重复了!)

然而,由于这可能很难理解,这里有一个使用您的代码(playground)的解决方案:

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)
    }
}

Go相关问答推荐

在保留额外参数的同时解封YAML

如何在使用中介资源时处理函数中的`defer`

即使HTTP服务器正在使用GO和Protobuf、SQL Server启动,请求也不返回结果

为什么(编码器).EncodeElement忽略";,innerxml";标记?

如何给杜松子wine 的路由加上一个名字,比如Laravel ?

Kafka架构注册表-Broker:Broker无法验证记录

如何根据中间件的请求设置上下文值?获取 go-staticcheck 问题

为什么 mux.Vars() 返回空的 map[]

如何为ANTLR4目标Go调试监听器

Go 中的 YAML 自定义标签

为什么 net/http 不遵守超过 30 秒的超时持续时间?

同一文件上的多个 Arrow CSV 阅读器返回 null

我的神经网络(从头开始)训练,让它离目标更远

regex.ReplaceAll 但如果替换则添加相同数量的字符

Golang Gin 绑定请求正文 XML 到 Slice

来自洪流公告的奇怪同行字段

有没有办法将 yaml node 添加到 golang 中现有的 yaml 文档中?

无法建立连接,因为目标机器主动拒绝它 Golang

如何 Select 前 N 个元素 Gin-Gorm

防止在 Go 公用文件夹中列出目录