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

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

1️⃣ Как кратко ответить

Используйте метод Done() объекта context.Context, чтобы прекратить выполнение кода при остановке контекста. Проверяйте канал, возвращаемый Done(), в цикле или с помощью select, чтобы определить, когда контекст отменен, и завершить выполнение.

2️⃣ Подробное объяснение темы

В Go контексты (context.Context) используются для управления временем жизни операций, особенно в асинхронных или сетевых запросах. Они позволяют отменять операции, когда они больше не нужны, например, при истечении времени ожидания или закрытии соединения. Это помогает избежать утечек ресурсов и улучшает отзывчивость приложения.

Пример использования контекста

Рассмотрим пример, где мы хотим прекратить выполнение функции, если контекст был отменен:

package main
​
import (
    "context"
    "fmt"
    "time"
)
​
func main() {
    // Создаем контекст с тайм-аутом 2 секунды
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    defer cancel() // Освобождаем ресурсы, связанные с контекстом
​
    // Запускаем функцию, которая будет выполняться до отмены контекста
    go doWork(ctx)
​
    // Ждем завершения работы
    time.Sleep(3 * time.Second)
}
​
func doWork(ctx context.Context) {
    for {
        select {
        case <-ctx.Done():
            // Контекст был отменен, прекращаем выполнение
            fmt.Println("Контекст отменен:", ctx.Err())
            return
        default:
            // Выполняем работу
            fmt.Println("Работаем...")
            time.Sleep(500 * time.Millisecond) // Имитируем работу
        }
    }
}

Объяснение кода

  1. Создание контекста с тайм-аутом:

    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    

    Здесь мы создаем контекст с тайм-аутом 2 секунды. context.WithTimeout возвращает новый контекст и функцию cancel, которую следует вызвать для освобождения ресурсов, связанных с контекстом.

  2. Отложенный вызов cancel():

    defer cancel()
    

    Используем defer для вызова cancel() при завершении функции main, чтобы гарантировать освобождение ресурсов.

  3. Запуск функции doWork:

    go doWork(ctx)
    

    Запускаем функцию doWork в отдельной горутине, передавая ей контекст.

  4. Цикл в doWork:

    for {
        select {
        case <-ctx.Done():
            fmt.Println("Контекст отменен:", ctx.Err())
            return
        default:
            fmt.Println("Работаем...")
            time.Sleep(500 * time.Millisecond)
        }
    }
    

    Внутри функции doWork мы используем select, чтобы постоянно проверять канал ctx.Done(). Когда контекст отменяется (например, по истечении тайм-аута), канал Done() закрывается, и мы выходим из функции, прекращая выполнение.

  5. Имитируем работу:

    time.Sleep(500 * time.Millisecond)
    

    В блоке default мы имитируем выполнение работы, используя time.Sleep.

Зачем это нужно

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

Тема: Конкурентность
Стадия: Tech

🔒 Подпишись на бусти автора и стань Алигатором, чтобы получить полный доступ к функционалу сайта и отслеживать свой прогресс!

Твои заметки