Как прекратить исполнение кода при остановке контекста?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Управление выполнением при отмене контекста в Go
В Go контексты (context) являются стандартным механизмом для передачи метаданных, дедлайнов и сигналов отмены между горутинами. Правильная обработка отмены контекста критически важна для создания отзывчивых и ресурсоэффективных приложений.
Основные подходы к прекращению выполнения
1. Периодическая проверка через context.Done()
Наиболее распространенный подход — регулярная проверка канала Done() в критических точках выполнения:
func process(ctx context.Context, data []byte) error {
for i := 0; i < len(data); i++ {
select {
case <-ctx.Done():
return ctx.Err() // Возвращаем причину отмены
default:
// Выполняем работу
result := expensiveOperation(data[i])
// Дополнительная проверка в долгих операциях
if err := ctx.Err(); err != nil {
cleanup()
return err
}
}
}
return nil
}
2. Использование горутин с отменяемыми операциями
Для долгих операций лучше использовать отдельные горутины:
func longRunningTask(ctx context.Context) error {
resultChan := make(chan int)
errChan := make(chan error)
go func() {
// Имитация долгой операции
time.Sleep(5 * time.Second)
resultChan <- 42
}()
select {
case <-ctx.Done():
return fmt.Errorf("operation cancelled: %w", ctx.Err())
case result := <-resultChan:
fmt.Printf("Result: %d\n", result)
return nil
case err := <-errChan:
return err
}
}
3. Прерывание блокирующих операций
Для операций, которые блокируются на чтении/записи:
func networkRequest(ctx context.Context, url string) ([]byte, error) {
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return nil, err
}
client := &http.Client{}
resp, err := client.Do(req) // Автоматически прервется при отмене ctx
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
return io.ReadAll(resp.Body)
}
Практические паттерны
Паттерн "Worker с немедленной остановкой"
func worker(ctx context.Context, tasks <-chan Task) {
for {
select {
case <-ctx.Done():
// Немедленно прекращаем выполнение
return
case task, ok := <-tasks:
if !ok {
return // Канал задач закрыт
}
processTask(task)
}
}
}
Паттерн "Graceful shutdown с таймаутом"
func serveWithGracefulShutdown(ctx context.Context, server *http.Server) {
go func() {
<-ctx.Done() // Ждем сигнала отмены
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
server.Shutdown(shutdownCtx) // Graceful shutdown
}()
server.ListenAndServe()
}
Важные моменты
Обязательные действия при обработке отмены:
- Всегда возвращайте
ctx.Err()для информирования о причине отмены - Освобождайте ресурсы (закрывайте файлы, соединения)
- Отменяйте дочерние операции, созданные с тем же контекстом
- Логируйте отмену операций для отладки
Типичные ошибки:
- Игнорирование возвращаемого значения
ctx.Err() - Проверка контекста только в начале функции
- Неправильная обработка вложенных контекстов
Оптимизация производительности
Для high-load систем минимизируйте проверки контекста в горячих путях, но не забывайте о них в долгих операциях. Используйте дедлайны (deadlines) для автоматического прерывания операций:
func operationWithDeadline(parentCtx context.Context) error {
ctx, cancel := context.WithTimeout(parentCtx, 2*time.Second)
defer cancel()
// Операция автоматически отменится через 2 секунды
return performOperation(ctx)
}
Правильное использование контекстов для управления выполнением кода — основа создания отказоустойчивых и масштабируемых Go-приложений, корректно реагирующих на сигналы остановки и прерывания.