← Назад к вопросам

Как прекратить исполнение кода при остановке контекста?

1.0 Junior🔥 71 комментариев
#Основы Go

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Управление выполнением при отмене контекста в 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-приложений, корректно реагирующих на сигналы остановки и прерывания.

Как прекратить исполнение кода при остановке контекста? | PrepBro