Fire‑and‑forget в Go: практический гайд по управляемым фоновым задачам

kmoseenk 43 минуты назад Fire‑and‑forget в Go: практический гайд по управляемым фоновым задачам Средний 9 мин 1.4K Блог компании OTUS Go * Программирование * Туториал Перевод Автор оригинала: Redowan Delowar Сейчас...
<5 — 2026'da uzaya kaç SpaceX Starship fırlatması ulaşacak?
Вот важная новость с фронта ИИ: kmoseenk 43 минуты назад Fire‑and‑forget в Go: практический гайд по управляемым фоновым задачам Средний 9 мин 1. 4K Блог компании OTUS Go * Программирование * Туториал Перевод Автор оригинала: Redowan Delowar Сейчас неконтролируемые вызовы go func() встречаются уже не так часто, как на заре Go. Наверняка многие знают о принципе: «Никогда не запускайте горутину, если не знаете, как она завершится».
И всё же мне до сих пор время от времени попадаются несинхронизированные вызовы go func(), которые запускают задачи без ожидания результата. Я не говорю о заданиях, переданных в специализированную очередь задач вроде Asynq. В таком случае система управления задачами сама отвечает за жизненный цикл запущенных задач.
Технические детали
Речь о небольших best‑effort‑задачах, для которых особенно велик соблазн просто вызвать go func() и забыть о них. Например, так часто отправляют уведомления или записывают ресурсоёмкие диагностические логи. Обычно это выглядит так:func (h *handler) createOrder(w http.
ResponseWriter, r *http. Request) { user := r. Context() go func() { h.
SendNotification(ctx, user) // (1) }() go func() { h. WriteDiagnosticLog(ctx, user) // (2) }() w. StatusAccepted) }В обработчике выше:В фоновой горутине отправляется уведомление.
Отраслевые последствия
В другой фоновой горутине записывается диагностический лог. Ответ не ждёт завершения ни одной из этих операций. Для долго работающего сервера это может показаться вполне разумным: скорее всего, main будет жить дольше обоих вызовов.
Но у такого подхода есть несколько проблем:каждый запрос может запустить ещё две горутины, при этом количество одновременно выполняемых задач никак не ограничено;обе задачи получают r. Context(), а net/http отменяет его после завершения обработчика. Если задачи учитывают отмену — а в идеале так и должно быть, — они могут завершиться, не успев выполнить работу до конца;паника в любой из горутин завершит весь процесс, поскольку её никто не перехватывает;во время завершения работы main не может дождаться ни одной из задач.
При перезапуске все ещё выполняющиеся задачи будут оборваны. Все задачи проходят через один пул воркеровОдин из вариантов решения, которым я пользуюсь, — небольшой пул воркеров на основе буферизованного канала:каждая фоновая задача отправляется в канал в виде замыкания func();воркеры запускаются в точке сборки приложения, обычно в main, вычитывают задачи из канала и выполняют замыкания;количество воркеров фиксировано, поэтому уровень параллелизма остаётся ограниченным;никакой другой код в приложении не запускает фоновые горутины: вся фоновая работа проходит через этот пул;при завершении работы main закрывает канал.
Этот прогресс даёт важные сигналы о будущем отрасли, и технологический мир внимательно наблюдает.





