Назад к вопросам
Junior
132
questionbank

Что такое контекст в Go?

Sobes Copilot

Получайте ответы в реальном времени

Ответ от нейросети

sobes.tech AI

Контекст (context.Context) в Go — объект, который несет дедлайн, сигнал отмены (cancellation signal) и данные, специфичные для запроса. Используется для управления жизненным циклом горутин и передачи информации между ними в процессе выполнения одного запроса/операции.

Основные характеристики:

  • Потокобезопасен: Может безопасно передаваться между горутинами.
  • Иммутабелен: Once created, a Context cannot be changed. Деривативные контексты создаются на основе родительского.
  • Иерархичен: Контексты образуют дерево с корнем (context.Background() или context.TODO()). Отмена родительского контекста приводит к отмене всех дочерних.

Основные функции пакета context:

  • context.Background(): Пустой контекст, обычно используется в main-функции, в верхнеуровневых запросах или при тестировании. Не отменен никогда.
  • context.TODO(): Также пустой контекст. Используется как заглушка, когда неизвестно, какой контекст использовать, или если функциональность контекста еще не реализована.
  • context.WithCancel(parent Context): Возвращает новый дочерний контекст и функцию отмены. Вызов функции отмены отменяет этот контекст и все его дочерние контексты.
  • context.WithDeadline(parent Context, deadline time.Time): Возвращает дочерний контекст, который автоматически отменяется, когда наступает указанное время. Также возвращает функцию отмены для ручной отмены.
  • context.WithTimeout(parent Context, timeout time.Duration): То же, что и WithDeadline, но с указанием длительности.
  • context.WithValue(parent Context, key, val interface{}): Возвращает дочерний контекст с присвоенным значением по ключу.

Применение:

  • Отмена операций: Например, таймаут HTTP-запроса или прерывание длительной операции.
  • Передача данных: Например, ID запроса для логирования, аутентификационная информация.

Пример использования context.WithTimeout:

package main

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

func longRunningOperation(ctx context.Context, resultChan chan string) {
	select {
	case <-time.After(3 * time.Second):
		// Операция завершилась успешно
		resultChan <- "Operation completed"
	case <-ctx.Done():
		// Контекст был отменен
		err := ctx.Err() // Получаем причину отмены
		resultChan <- fmt.Sprintf("Operation cancelled: %v", err)
	}
}

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

	resultChan := make(chan string, 1)

	go longRunningOperation(ctx, resultChan)

	// Ждем результат или отмену
	select {
	case res := <-resultChan:
		fmt.Println(res)
	case <-ctx.Done():
		fmt.Printf("Main context done: %v\n", ctx.Err())
	}

	// Ждем немного, чтобы горутина успела завершиться, если была отменена
	time.Sleep(1 * time.Second)
}

В этом примере longRunningOperation проверяет контекст на отмену. Если таймаут основного контекста (2 секунды) истекает раньше, чем завершится itself (3 секунды), горутина получает сигнал отмены через ctx.Done() и завершается.