Ответ
Горутина отслеживает отмену контекста через специальный канал, доступный по методу ctx.Done()
. Когда контекст отменяется (например, по тайм-ауту или прямому вызову функции cancel
), Go закрывает этот канал.
Идиоматичный способ проверки — использовать select
внутри цикла, чтобы не блокировать выполнение основной логики горутины.
Основные моменты:
<-ctx.Done()
: Чтение из этого канала блокируется до тех пор, пока контекст не будет отменен. Как только он отменяется, канал закрывается, и чтение из него немедленно завершается, возвращая нулевое значение.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)
}