在延迟执行后,从子Go协程打印值。

huangapple go评论76阅读模式
英文:

value printing from child go routine after defer

问题

我正在从我的驱动代码中生成5个工作池,并从工作池返回错误。在我的主函数中,我有另一个Go协程(命名为Go协程A,在该Go协程顶部添加了注释),用于监听错误。

但是在从错误通道中获取数据的时候,我的延迟语句已经执行了。但我仍然可以看到来自Go协程A的日志。

我怀疑延迟语句被执行后,我的主要Go协程完成,然后执行了返回语句。但是,我该如何在返回之前传播我从multiErrors中附加的错误?

如果我尝试使用通道同步Go协程A和我的主要Go协程(调用延迟语句的那个),它会变得阻塞。希望得到帮助。

英文:

I am spwanning 5 worker pools from my driver code, and returning errors from worker pools. In my main, i have another go routine (go routine A , added comment on top of that go routine)listening on errors.
But by the time data is picked from my error channel, my defer statement is getting executed. But i can still see logs from 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
	}
}

}

And my logs go like this

info spawnning worker 0
2022/03/14 01:51:44 info spawnning worker 2
2022/03/14 01:51:44 info spawnning worker 1
2022/03/14 01:51:44 info done working
2022/03/14 01:51:44 info done working
2022/03/14 01:51:44 info spawnning worker 3
2022/03/14 01:51:44 info done working
2022/03/14 01:51:44 info spawnning worker 4
2022/03/14 01:51:44 info done working
2022/03/14 01:51:44 info pushing error from 0
2022/03/14 01:51:44 info done working
2022/03/14 01:51:44 error blhgsgh 0 <nil>
2022/03/14 01:51:44 error got erro1r 1 error occurred:
* myError

I kind of suspect defer got executed, then my main go routine finished and then return got executed, but what i can i do to propagate the error i am appending from multiErrors before returning?

if i try to synchronise go routine A and my main go routine(the one where i call defer) using channels, it becomes blocking.Help appretiated

答案1

得分: 0

我已经在playground中简化了你的代码。

你似乎假设当f.Manager.Wg.Wait()返回时,所有的错误都已经被处理完毕。然而,错误是在一个单独的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()

// 所有发送到errorChan的操作现在都已完成,所以我们可以安全地关闭通道
close(errorChan)
<-errDone // 等待错误处理的goroutine完成

请注意,defer语句在“环绕函数返回之前立即运行”。你启动的任何goroutine都可以超出函数的生命周期(它们不会自动停止)。

英文:

I have simplified your code in the playground.

You appear to be assuming that when f.Manager.Wg.Wait() returns all errors will have been processed. However the errors are being processed in a separate goroutine (**//go routine A**) and you are not waiting for that to complete -- in fact because you do not close f.Manager.ErrorChan the goroutine never completes.

The simplest way to resolve this is to wait for the goroutine to exit before returning from the function. The below example (playground) uses a channel to do this but you could also use a WaitGroup if you prefer.

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

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

Note that defer runs "immediately before the surrounding function returns". Any goroutines that you have started can outlive the function (they are not automatically stopped).

huangapple
  • 本文由 发表于 2022年3月14日 04:32:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/71460611.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定