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

Какой контекст у горутины?

2.0 Middle🔥 261 комментариев
#Конкурентность и горутины#Основы Go

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

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

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

Контекст горутины в Go

В Go горутина не имеет собственного выделенного контекста по умолчанию. Это фундаментальное отличие от некоторых других языков, где потоки могут иметь TLS (Thread-Local Storage). Однако контекст (в виде пакета context) — это ключевой механизм для управления жизненным циклом, отменой операций и передачей данных в рамках цепочки вызовов, особенно в горутинах.

Что передаётся в горутины?

При запуске горутины она наследует некоторые аспекты окружения, но не всё:

  • Глобальные переменные — доступны, но требуют синхронизации (через sync.Mutex, каналы и т.д.).
  • Лексическое замыкание — горутина захватывает переменные из окружающей области видимости.
  • Аргументы функции, переданные при вызове go func(...).
  • Контекст — обычно передаётся явно как первый аргумент функции.
package main

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

func worker(ctx context.Context, id int) {
    for {
        select {
        case <-ctx.Done():
            fmt.Printf("Worker %d: canceled via context\n", id)
            return
        default:
            fmt.Printf("Worker %d: working...\n", id)
            time.Sleep(500 * time.Millisecond)
        }
    }
}

func main() {
    // Создаём контекст с отменой
    ctx, cancel := context.WithCancel(context.Background())
    
    // Запускаем горутины с явной передачей контекста
    for i := 1; i <= 3; i++ {
        go worker(ctx, i)
    }
    
    // Даём поработать 2 секунды
    time.Sleep(2 * time.Second)
    
    // Отменяем все горутины через контекст
    cancel()
    
    // Даём время на завершение
    time.Sleep(500 * time.Millisecond)
}

Ключевые аспекты работы с контекстом в горутинах

1. Передача контекста явно

Контекст всегда должен передаваться как первый параметр в функции, которые могут быть отменены или требуют таймаутов. Это идиома Go.

2. Типы контекстов и их применение

  • context.Background() — корневой контекст, обычно используется в main() или тестах.
  • context.TODO() — заглушка, когда не ясно, какой контекст использовать.
  • context.WithCancel() — для ручной отмены.
  • context.WithTimeout() — для отмены по таймауту.
  • context.WithDeadline() — для отмены к определённому времени.
  • context.WithValue() — для передачи данных (только для request-scoped данных!).

3. Механизмы отмены через контекст

Контекст позволяет координировать отмену операций:

func process(ctx context.Context, data chan int) {
    for {
        select {
        case <-ctx.Done():
            // Контекст отменён - выходим
            fmt.Println("Stopping processing")
            return
        case item := <-data:
            // Обработка данных
            fmt.Printf("Processing: %d\n", item)
        }
    }
}

4. Передача request-scoped данных

// Создание контекста с данными
type userKey struct{}
ctx := context.WithValue(context.Background(), userKey{}, "user123")

// Извлечение в горутине
func handleRequest(ctx context.Context) {
    if user, ok := ctx.Value(userKey{}).(string); ok {
        fmt.Printf("User: %s\n", user)
    }
}

Практические рекомендации

  • Не храните контекст в структурах — передавайте его как аргумент в методы.
  • Всегда проверяйте ctx.Done() в долго работающих операциях.
  • Закрывайте производные контексты через defer cancel() для избежания утечек.
  • Используйте context.Value() экономно — только для request-scoped данных, не для передачи обязательных параметров.
  • Контекст должен пронизывать все уровни вызовов в рамках одной логической операции.

Отличие от других языков

В отличие от Java ThreadLocal или C++ thread_local, в Go нет встроенного механизма хранения данных, привязанных к конкретной горутине. Это сделано сознательно для упрощения модели и избежания скрытых зависимостей. Вместо этого используются:

  • Явная передача аргументов
  • Замыкания
  • Контекст для request-scoped данных

Таким образом, хотя у горутины нет собственного "магического" контекста, пакет context предоставляет мощный и стандартизированный способ управления жизненным циклом операций, что особенно критично в конкурентных средах с множеством горутин.