Sobes.tech
Middle

Fale sobre os casos de uso do contexto em Go.

sobes.tech IA

Resposta da IA

O contexto em Go é utilizado para gerir o ciclo de vida das requisições e controlar a execução das goroutines. Os principais casos de uso:

  • Cancelamento: Permite cancelar a cadeia de goroutines relacionada com uma requisição específica. Por exemplo, ao fechar a conexão ou por timeout.
  • Timeout: Define um prazo limite para a operação. Se a operação não terminar nesse prazo, o contexto é cancelado. Isto evita esperas infinitas.
  • Transferência de dados específicos da requisição: Permite passar dados relacionados com uma requisição concreta (como ID de utilizador, identificador de traçado) através da cadeia de chamadas sem alterar as suas assinaturas.

Exemplos de uso:

  1. Cancelamento por sinal:

    package main
    
    import (
    	"context"
    	"fmt"
    	"time"
    )
    
    func worker(ctx context.Context, id int) {
    	for {
    		select {
    		case <-ctx.Done():
    			fmt.Printf("Trabalhador %d interrompido\n", id)
    			return
    		default:
    			fmt.Printf("Trabalhador %d a trabalhar...\n", id)
    			time.Sleep(500 * time.Millisecond)
    		}
    	}
    }
    
    func main() {
    	ctx, cancel := context.WithCancel(context.Background())
    	defer cancel() // Garantir cancelamento
    
    	go worker(ctx, 1)
    	go worker(ctx, 2)
    
    	time.Sleep(2 * time.Second)
    	cancel() // Cancela o contexto
    
    	time.Sleep(500 * time.Millisecond) // Dá tempo às goroutines para terminar
    }
    
  2. Timeout:

    package main
    
    import (
    	"context"
    	"fmt"
    	"time"
    )
    
    func longOperation(ctx context.Context) error {
    	select {
    	case <-time.After(3 * time.Second): // Simulação de uma operação longa
    		fmt.Println("Operação concluída com sucesso")
    		return nil
    	case <-ctx.Done():
    		fmt.Println("Operação cancelada pelo contexto:", 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("Erro:", err)
    	}
    }
    
  3. Transferência de dados:

    package main
    
    import (
    	"context"
    	"fmt"
    )
    
    type UserKey string
    
    const userIDKey UserKey = "userID"
    
    func processRequest(ctx context.Context) {
    	userID := ctx.Value(userIDKey).(int)
    	fmt.Printf("Processando requisição para o ID do usuário: %d\n", userID)
    	// Processamento adicional da requisição usando userID
    }
    
    func main() {
    	ctx := context.WithValue(context.Background(), userIDKey, 123)
    	processRequest(ctx)
    }
    

É importante usar context.Background() ou context.TODO() como contexto raiz e passá-lo para baixo na cadeia de chamadas. Não se deve armazenar o contexto em estruturas de dados se ele não estiver diretamente relacionado com o ciclo de vida dessa estrutura.