Как горутина узнаёт об отмене контекста (context)?

Ответ

Горутина отслеживает отмену контекста через специальный канал, доступный по методу ctx.Done(). Когда контекст отменяется (например, по тайм-ауту или прямому вызову функции cancel), Go закрывает этот канал.

Идиоматичный способ проверки — использовать select внутри цикла, чтобы не блокировать выполнение основной логики горутины.

Основные моменты:

  1. <-ctx.Done(): Чтение из этого канала блокируется до тех пор, пока контекст не будет отменен. Как только он отменяется, канал закрывается, и чтение из него немедленно завершается, возвращая нулевое значение.
  2. ctx.Err(): После отмены контекста этот метод возвращает причину отмены: context.Canceled (явная отмена) или context.DeadlineExceeded (истек тайм-аут).

Пример реализации:

package main

import (
    "context"
    "fmt"
    "time"
)

// worker — функция, которая выполняет некую работу
// и периодически проверяет, не отменен ли контекст.
func worker(ctx context.Context) {
    for {
        select {
        case <-ctx.Done(): // Контекст отменен
            fmt.Println("Работа прервана, причина:", ctx.Err())
            return // Завершаем горутину
        default:
            // Выполняем полезную работу
            fmt.Println("Работаю...")
            time.Sleep(500 * time.Millisecond)
        }
    }
}

func main() {
    // Создаем контекст, который отменится через 2 секунды
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    defer cancel() // Важно вызывать cancel, чтобы освободить ресурсы

    go worker(ctx)

    // Ждем, пока контекст не будет отменен
    <-ctx.Done()
    fmt.Println("Основная горутина: контекст отменен.")
    // Даем воркеру немного времени, чтобы вывести сообщение о завершении
    time.Sleep(100 * time.Millisecond) 
}