Во время отмены запроса как не допустить, чтобы рабочая горутина навсегда заблокировалась при отправке результата?
Отправку результата нужно выполнять в select одновременно с ожиданием сигнала отмены. Если отмена наступила раньше успешной передачи результата, горутина выбирает ветку отмены и завершает работу, не зависая на отправке.
Горутины позволяют запускать конкурентные операции, но их жизненный цикл не завершается автоматически вместе с вызывающей функцией. Каналы и select дают простой механизм кооперативной отмены: одна сторона сообщает о завершении или отмене, а другая периодически выбирает между полезной работой и прекращением.
Такой подход решает проблему утечек горутин, возникающих, когда производитель пытается отправить результат, а потребитель уже перестал его получать. Для запросов обычно вместо отдельного канала используют context.Context, но принцип выбора между результатом и отменой остаётся тем же.
Рабочая горутина может завершить вычисление после того, как клиент отключился, запрос был отменён или вызывающая функция перестала ждать результат. Если отправка выполняется без альтернативной ветви, горутина может остаться заблокированной навсегда.
Это приводит к накоплению горутин, удержанию памяти и, возможно, занятых ресурсов: файлов, соединений или объектов из пулов. Простое закрытие канала результата не является универсальным решением: отправка в закрытый канал вызывает панику, поэтому закрывать его должен заранее определённый владелец, обычно единственный отправитель после завершения всех отправок.
Сигнал отмены представляют закрытием канала done или отменой контекста. Закрытие канала становится доступным для получения во всех ожидающих горутинах, поэтому один сигнал может оповестить множество участников.
Ветвь отправки готова только при наличии получателя либо свободного места в буферизованном канале. Ветвь чтения из закрытого done готова после его закрытия, поэтому select не оставляет горутину заблокированной на одной операции.
Если одновременно готовы обе ветви, select выбирает одну из них псевдослучайно. Поэтому успешная отправка результата всё ещё возможна после того, как отмена уже наступила; если это недопустимо по контракту, перед отправкой требуется дополнительная проверка состояния отмены, хотя между проверкой и отправкой всё равно остаётся竞курентное окно.
Для реального кода обычно передают context.Context и выбирают между result <- value и <-ctx.Done(). Отмена является кооперативной: она не прерывает горутину принудительно, поэтому длительные вычисления также должны периодически проверять сигнал отмены.
Компромисс состоит в том, что отменённый результат может быть потерян, зато система не удерживает завершившуюся операцию бесконечно. Буферизация уменьшает вероятность блокировки, но не устраняет её при заполнении буфера и не заменяет обработку отмены.
Сервис запускает вычисление для HTTP-запроса. Клиент закрывает соединение, пока вычисление ещё выполняется, а обработчик больше не читает канал результата.
Вариант с безусловной отправкой прост, но оставляет рабочую горутину заблокированной. Увеличение буфера снижает вероятность блокировки при небольшом числе результатов, однако требует памяти и не помогает, если результаты производятся быстрее потребителя.
Выбран вариант с передачей контекста запроса в рабочую функцию и select при отправке результата. Обработчик прекращает ожидание при отмене контекста, рабочая горутина получает тот же сигнал и освобождает ресурсы; в результате число зависших горутин не растёт при массовых отключениях клиентов.
Нет. Отмена может произойти во время вычисления или после него, непосредственно перед отправкой результата. Проверка только в начале защищает лишь от уже отменённых задач; безопасная отправка требует выбора между передачей результата и сигналом отмены. Для долгой работы проверку также нужно выполнять внутри вычислительного цикла.
Обычно нет. Закрытие канала означает, что новые отправки запрещены, и конкурентная отправка после этого вызовет панику. Канал результата должен закрывать его владелец по согласованному протоколу, а отмена запроса должна передаваться отдельным сигналом или через контекст.
select отказ от отправки, если отмена уже произошла?Не всегда. Если канал результата также готов к отправке, обе ветви доступны, и select может выбрать отправку. Это корректно, если после отмены допустимо принять уже готовый результат; если контракт требует строгого приоритета отмены, одной конструкции select недостаточно — нужно проектировать протокол так, чтобы получатель дополнительно проверял состояние отмены и не использовал результат после неё.