Назад к вопросам
Middle
104
questionbank

Расскажи о случаях использования контекста в Go.

Sobes Copilot

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

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

sobes.tech AI

Контекст в Go используется для управления жизненным циклом запросов и контроля выполнения горутин. Основные случаи применения:

  • Отмена: Позволяет отменить выполнение цепочки горутин, связанной с определенным запросом. Например, при закрытии соединения или по таймауту.
  • Таймаут: Устанавливает крайний срок выполнения операции. Если операция не завершается в срок, контекст отменяется. Это предотвращает бесконечное ожидание.
  • Передача запросо-специфичных данных: Позволяет передавать данные, относящиеся к конкретному запросу (например, ID пользователя, идентификатор трассировки), через цепочку вызовов функций без изменения их сигнатур.

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

  1. Отмена по сигналу:

    package main
    
    import (
    	"context"
    	"fmt"
    	"time"
    )
    
    func worker(ctx context.Context, id int) {
    	for {
    		select {
    		case <-ctx.Done():
    			fmt.Printf("Worker %d interrupted\n", id)
    			return
    		default:
    			fmt.Printf("Worker %d working...\n", id)
    			time.Sleep(500 * time.Millisecond)
    		}
    	}
    }
    
    func main() {
    	ctx, cancel := context.WithCancel(context.Background())
    	defer cancel() // Гарантируем отмену
    
    	go worker(ctx, 1)
    	go worker(ctx, 2)
    
    	time.Sleep(2 * time.Second)
    	cancel() // Отменяем контекст
    
    	time.Sleep(500 * time.Millisecond) // Даем время горутинам завершиться
    }
    
  2. Таймаут:

    package main
    
    import (
    	"context"
    	"fmt"
    	"time"
    )
    
    func longOperation(ctx context.Context) error {
    	select {
    	case <-time.After(3 * time.Second): // Имитация долгой работы
    		fmt.Println("Operation completed successfully")
    		return nil
    	case <-ctx.Done():
    		fmt.Println("Operation cancelled by context:", ctx.Err())
    		return ctx.Err()
    	}
    }
    
    func main() {
    	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    	defer cancel()
    
    	err := longOperation(ctx)
    	if err != nil {
    		fmt.Println("Error:", err)
    	}
    }
    
  3. Передача данных:

    package main
    
    import (
    	"context"
    	"fmt"
    )
    
    type UserKey string
    
    const userIDKey UserKey = "userID"
    
    func processRequest(ctx context.Context) {
    	userID := ctx.Value(userIDKey).(int)
    	fmt.Printf("Processing request for user ID: %d\n", userID)
    	// Дальнейшая обработка запроса с использованием userID
    }
    
    func main() {
    	ctx := context.WithValue(context.Background(), userIDKey, 123)
    	processRequest(ctx)
    }
    

Важно использовать context.Background() или context.TODO() в качестве корневого контекста и передавать его вниз по цепочке вызовов. Не следует хранить контекст в структурах данных, если он не связан напрямую с жизненным циклом этой структуры.