делать вложенные семафоры?
по какой причине
у меня в этом коде может вылететь c
ошибка
error can't acquire semaphore during Upload: context canceled ??
как может context.Background() быть отменен?
s := semaphore.NewWeighted(int64(b.cfg.General.UploadConcurrency))
g, ctx := errgroup.WithContext(context.Background())
for i, table := range tablesForUpload {
if err := s.Acquire(ctx, 1); err != nil {
return fmt.Errorf("can't acquire semaphore during Upload: %v", err)
}
попробуйте переписать код на использование канала, думаю сразу найдете в чем проблема
Контекст внутри errgroup может быть отменён из того что одна из тасок, запущенных в errgroup, вернула ошибку
Зачем отдельная либа для семафора вообще? Семафор же на буферизированном канале легко пишется
а кем он может быть отменен? как бы это узнать?
если надо уметь отменять ожидание - это уже не так легко
контекст проверять надо
ну так это и есть обёртка но можно по идее и на sync.Cond сделать
Опять же переписать с errgroup на waitgroup :) Вообще главное правило при встрече с непонятным багом - выкидывать нафиг не нужные абстракции типа семаформа и errgroup и максимально все упрощать и в какой то момент станет понятно что происходит
спасибо за совет все переписать... но как бы errorgroup мне как раз тут в канале и посоветовали =) задача у меня достаточно простая была, мне нужен пулл горутин с контроллируемым кол-вом одновременно запущеных... и возможностью узнать что одна из go routine вернула ошибку...
так, стоп, а разве это не будет отображаться вот в таком коде if err := g.Wait(); err != nil { return fmt.Errorf("one of upload go-routine return error: %v", err) } ?
ну если вы не справились с инструментом, то наверное все таки сначала стоит попробовать решить задачу без этого инструмента, посмотреть в чем проблема и уже потом наворачивать сверху errgroup и семафор
использую и еще надо gctx.Done() читать, иначе нет смысла юзать WithContext.
а как у вас это в коде выглядит?
если горутина из errorgroup читает из другого канала и какая-то одна из горутин завершилась ошибкой, то остальные горутины могут утечь.
не утекут, если вы Wait вызываете
Вот прямо как в этом примере - https://pkg.go.dev/golang.org/x/sync/errgroup#example-Group-Pipeline for i := 0; i < numDigesters; i++ { g.Go(func() error { for path := range paths { data, err := ioutil.ReadFile(path) if err != nil { return err } select { case c <- result{path, md5.Sum(data)}: case <-ctx.Done(): return ctx.Err() } } return nil }) }
Ну так Acquire внутри это и делает в коде выше
Да, вы правы
то есть я правильно понимаю, что если возникла ошибка в какой то go routine но при этом g.Wait еще не стартанул, то context canceled поймается на s.Acquire ?
при возврате ошибки из коллбэка в Go, контекст, который возвращает WithContext,будет отменён
это я понял но почему это отлавливается в semaphore.Acquire а не в errorgroup.Wait ?
а вы ошибку не возвращаете?
еще как возвращаю но может быть проблема в том что я делаю g.Go(func() error { defer s.Release(1) ?
и все таки, правильно ли делать defer s.Release(1) ? или все таки нужно аккуратно перед return err и return nil релизить семафор?
Даниил, а вы случайно не подскажете как правильно все таки семафор с ограниченной пропускной способностью + errorgroup использовать? сейчас навтыкал себе везде дебаг логов действительно горутина возвращает ошибку но я почему то думал наивно что ошибка прилетит в g.Wait() (errorgroup) который после цикла в котором g.Go() порождает горутины вызывается а прилетает сразу в семафор =)
не, я ничего про них не знаю а оно точно нужно?
Вернуть можно и через канал что-то... Не уверен что это правильная ссылка https://play.golang.org/p/hNaeTjLwdv по быстрому нагуглил.
Обсуждают сегодня