Sobes.tech
Middle

Como são estruturados os contextos na linguagem Go?

sobes.tech IA

Resposta da IA

O contexto (context.Context) em Go é usado para transmitir informações sobre prazos, cancelamentos e outros dados entre limites de API. Ele representa uma árvore: cada contexto é filho de outro, exceto o raiz (context.Background() ou context.TODO()).

Os métodos principais da interface context.Context:

  • Deadline() (deadline time.Time, ok bool): Retorna o prazo do contexto.
  • Done() <-chan struct{}: Retorna um canal que é fechado quando o contexto é cancelado.
  • Err() error: Retorna o erro que causou o cancelamento do contexto.
  • Value(key any) any: Retorna o valor associado à chave no contexto.

São criados com funções do pacote context:

  • context.Background(): O contexto raiz vazio, usado como base para o processo principal.
  • context.TODO(): Usado como marcador de posição quando não se sabe qual contexto usar.
  • context.WithCancel(parent Context): Cria um contexto filho e uma função de cancelamento.
  • context.WithDeadline(parent Context, d time.Time): Cria um contexto filho com um prazo.
  • context.WithTimeout(parent Context, timeout time.Duration): Cria um contexto filho com um tempo limite.
  • context.WithValue(parent Context, key, val any): Cria um contexto filho com um valor.

Implementações principais de Context:

  • emptyCtx: Básico, não cancelável e sem valores. Usa Background e TODO.
  • cancelCtx: Suporta cancelamento. Consiste em um contexto base e um canal de cancelamento.
  • timerCtx: Suporta prazo/tempo limite. Contém cancelCtx e um temporizador.
  • valueCtx: Suporta transmissão de valores. Contém um contexto base e pares chave-valor.

Ao cancelar o contexto pai, todos os filhos são cancelados automaticamente. Isso permite propagar sinais de cancelamento ou prazos na cadeia de chamadas.

Exemplo de uso de context.WithCancel:

package main

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

func main() {
	// Cria o contexto raiz e a função de cancelamento
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel() // Garante o cancelamento ao sair de main

	go func() {
		select {
		case <-ctx.Done(): // Aguarda o cancelamento do contexto
			fmt.Println("Tarefa cancelada:", ctx.Err())
			return
		case <-time.After(5 * time.Second): // Ou termina em 5 segundos
			fmt.Println("Tarefa concluída")
		}
	}()

	// Cancela o contexto de outro lugar
	time.Sleep(1 * time.Second)
	fmt.Println("Cancelando o contexto...")
	cancel()

	// Dá tempo para a goroutine processar o cancelamento
	time.Sleep(100 * time.Millisecond)
}

Exemplo de uso de context.WithValue:

package main

import (
	"context"
	"fmt"
)

type userIDKey string

func processRequest(ctx context.Context) {
	if userID, ok := ctx.Value(userIDKey("UserID")).(string); ok {
		fmt.Println("Processando requisição para User ID:", userID)
	} else {
		fmt.Println("User ID não encontrado no contexto")
	}
}

func main() {
	ctx := context.WithValue(context.Background(), userIDKey("UserID"), "12345")
	processRequest(ctx)
}

Os contextos são seguros para uso concorrente e imutáveis, o que é importante para código concorrente. Eles não são destinados a transmitir parâmetros brutos de funções, mas sim para metainformação que influencia a execução do processo.