Pull to refresh

Comments 12

Просто предупреждение. errgroup.WithContext не прерывает errgroup если родительский контекст закончился.

Разве прерывание родительского контекста не вызывает прерывания всех его дочерних контекстов?

Да все так. Прерывание дочерних пройдёт. Но errgroup даже с такой семантикой не будет реагировать. Надо в .Do самому контексты обрабатывать.

А можете привести пример?

Вот например такой код работает нормально
func main() {
ctx, done := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL)
defer done()
g, gctx := errgroup.WithContext(ctx)

// just a ticker every 2s g.Go(func() error { ticker := time.NewTicker(2 * time.Second) i := 0 for { i++ if i > 10 { return nil } select { case <-ticker.C: fmt.Println("ticker 2s ticked") case <-gctx.Done(): fmt.Println("closing ticker 2s goroutine") return gctx.Err() } } }) // just a ticker every 1s g.Go(func() error { ticker := time.NewTicker(1 * time.Second) i := 0 for { i++ if i > 10 { return nil } select { case <-ticker.C: fmt.Println("ticker 1s ticked") case <-gctx.Done(): fmt.Println("closing ticker 1s goroutine") return gctx.Err() } } }) // wait for all errgroup goroutines go func() { err := g.Wait() if err != nil { if errors.Is(err, context.Canceled) { fmt.Println("context was canceled") } else { fmt.Printf("received error: %v\n", err) } } else { fmt.Println("finished clean") } }() time.Sleep(5 * time.Second) fmt.Println("before done") done() fmt.Println("after done") time.Sleep(1 * time.Second) }

Вывод:
ticker 1s ticked
ticker 1s ticked
ticker 2s ticked
ticker 1s ticked
ticker 2s ticked
ticker 1s ticked
before done
closing ticker 1s goroutine
closing ticker 2s goroutine
context was canceled
after done

/

Все верно как и описал - надо обрабатывать контекст явно.

Представь что у тебя в errgroup.Go код который не работает через select и одновременно не может обрабатывать ctx - к примеру чтото типа Listen или еще чегото не асинхронное (к примеру syscall). Родительский ctx может быть остановлен по таймауту но errgroup.Wait все также будет держать управление, пока ктото из группы не вернет ошибку.

А, теперь понял. Да согласен, это надо учитывать. Спасибо, что подсветил.

По нынешним временам многопоточность-то в значительной степени мигрировала за пределы приложений. Микросервисы в кубере общаются внешними очередями... всё это уже ортогонально конкретному языку. Горутины и мессаджи мы по-прежнему используем для всяких мелких нужд, но если приходится пилить что-то более мудрёное чем отложенная обработка последствий хттп-запроса - надо себя спросить, всё ли я правильно тут наархитектурил.

По нынешним временам многопоточность-то в значительной степени мигрировала за пределы приложений.

Поэтому, если у тебя тормозит текстовый редактор, то запусти несколько штук и работай с ними параллельно.

Этот подход удобен тем, что позволяет избежать блокировки основного потока выполнения, особенно если задача требует длительного времени. Ты просто запускаешь задачу, делаешь другие вещи, а затем, когда задача завершена, возвращаешься к ее результату.

Но в примере Feature/Promise вроде же блокируется главный поток на строке result := <-future.

Неправильный пример?

Для получения результата так или иначе ты должен заблокировать основной поток. В примере автора просто нет имитации выполнения полезных действий между запуском задачи и получением из неё результата, поэтому кажется что пример максимально высосан из пальца.

А так ли нужно использовать буферизованный канал? Видится что промис закончил работу и вполне может заблокироваться в ожидании пока вычитают resultCh. Советуют без лишней необходимости не использовать буферизованные каналы. И тут ее вроде как и нет. Сам паттерн не подразумевает усложнения промиса.

Если бы канал был не буферизованный, то горутина, в которой происходит отправка результата в канал, была бы вынуждена дожидаться, пока значение, отправленное ею в канал, будет прочитано. Это потенциальный дедлок как минимум. О том, что лавинообразное создание таких горутин может привести у OOM, я и не говорю.

Поэтому да - если горутина, отправляющая в канал, должна ждать, когда из канала кто-то прочитает, то однозначно нужно использовать небуферизованный канал. Когда же горутина может отправить в канал и дальше себе работать - стоит создавать буферизованные каналы.

Sign up to leave a comment.

Articles