child 中的值打印在延迟后进入常规

value printing from child go routine after defer

我正在从我的驱动程序代码中生成 5 个工作池,return从工作池中查找错误。在我的主要任务中,我有另一个 go routine(go routine A ,在那个 go routine 之上添加了注释)监听错误。 但是当从我的错误通道中选取数据时,我的 defer 语句正在执行。但是我仍然可以看到 go routine A 的日志。

func ....{
var requests []Req
        err := json.Unmarshal(Data, &requests)
        if err != nil {
            log.WithError(err).Errorf("Invalid data passed for flag type %v", proto.CreateFlagReq_SET_OF.String())
            return err
        }
        f.Manager.TaskChan = make(chan Req, 100)
        f.Manager.ErrorChan = make(chan error, 100)

        for i := 0; i < f.Manager.WorkerCount; i++ {
            f.Manager.Wg.Add(1)
           //AddToSetOfcustomers just validates before addigg to redis
            go f.Manager.Work(ctx, f.redisPool.AddToSetOfcustomers, i)
        }

        for _, request := range requests {
            f.Manager.TaskChan <- request
        }
        close(f.Manager.TaskChan)

        var errors error
        **//go routine A**
        go func() {
            for {
                select {
                case err ,ok:= <- f.Manager.ErrorChan:
                    if ok{
                        errors = multierror.Append(errors, err)
                        log.Errorf("got erro1r %v",errors)
                    }else{
                        log.Info("returning")
                        return
                    }

                }
            }
        }()
        f.Manager.Wg.Wait()

        defer log.Errorf("blhgsgh   %v %v",len(f.Manager.ErrorChan),errors)
        return errors
}


func (m *Manager) Work(ctx context.Context, fn func(string, string, string) error, workerNumber int) {
log.Infof("spawnning worker %v", workerNumber)
defer m.Wg.Done()
defer log.Info("done working")
for {
    select {
    case t, ok := <-m.TaskChan:
        if ok {
            err := fn(t.CustomerName, t.CustomerId, t.Feature)
            if err != nil {
                log.Infof("pushing error from %v",workerNumber)
                m.ErrorChan <- err
            }
        } else {
            return
        }
    case <-ctx.Done():
        log.Infof("closing channel %v", ctx.Err())
        return
    }
}

}

我的日志是这样的

信息生成工人 0
2022/03/14 01:51:44 信息产卵工人 2
2022/03/14 01:51:44 信息产卵工人 1
2022/03/14 01:51:44 信息完成工作
2022/03/14 01:51:44 信息完成工作
2022/03/14 01:51:44 信息产卵工人 3
2022/03/14 01:51:44 信息完成工作
2022/03/14 01:51:44 信息产卵工人 4
2022/03/14 01:51:44 信息完成工作
2022/03/14 01:51:44 信息从 0
推送错误 2022/03/14 01:51:44 信息完成工作
2022/03/14 01:51:44 错误 blhgsgh 0
2022/03/14 01:51:44 错误发生 erro1r 1 错误: * 我的错误

我有点怀疑 defer 被执行了,然后我的主要 go 例程完成了,然后 return 被执行了,但是我能做些什么来传播我在 return 之前从 multiErrors 附加的错误正在?

如果我尝试使用通道同步 go 例程 A 和我的主要 go 例程(我调用 defer 的那个),它变得 blocking.Help appretiated

我在 the playground 中简化了您的代码。

您似乎假设当 f.Manager.Wg.Wait() returns 时所有错误都将得到处理。然而,错误是在一个单独的 goroutine (**//go routine A**) 中处理的,你不会等待它完成——事实上,因为你没有关闭 f.Manager.ErrorChan goroutine 永远不会完成。

解决此问题的最简单方法是等待 goroutine 退出,然后再从函数返回。下面的示例 (playground) 使用通道来执行此操作,但如果您愿意,也可以使用 WaitGroup

var errors []error
errDone := make(chan struct{})
go func() {
    for {
        select {
        case err, ok := <-errorChan:
            if ok {
                errors = append(errors, err)
                log.Printf("got error %v", errors)
            } else {
                log.Printf("returning")
                close(errDone)
                return
            }
        }
    }
}()
wg.Wait()

// Everything sending to errorChan is now done so we can safely close the channel
close(errorChan)
<-errDone // Wait for error handling goroutine to complete

请注意 defer“紧接在周围函数 returns 之前”运行。您启动的任何 goroutine 都可以比该函数存活更久(它们不会自动停止)。