Sobes.tech
Middle

Cum sunt structurate contexturile în limbajul Go?

sobes.tech AI

Răspuns de la AI

Contextul (context.Context) în Go este utilizat pentru a transmite informații despre termene limită, anulări și alte date între limitele API-ului. El reprezintă un arbore: fiecare context este un copil al altuia, cu excepția rădăcinii (context.Background() sau context.TODO()).

Metodele principale ale interfeței context.Context:

  • Deadline() (deadline time.Time, ok bool): Returnează termenul limită al contextului.
  • Done() <-chan struct{}: Returnează un canal care se închide când contextul este anulat.
  • Err() error: Returnează eroarea care a condus la anularea contextului.
  • Value(key any) any: Returnează valoarea asociată cu cheia în context.

Se creează cu funcții din pachetul context:

  • context.Background(): Contextul rădăcină gol, utilizat ca bază pentru procesul principal.
  • context.TODO(): Utilizat ca marcator temporar când nu se știe ce context să se folosească.
  • context.WithCancel(parent Context): Creează un context copil și o funcție de anulare.
  • context.WithDeadline(parent Context, d time.Time): Creează un context copil cu termen limită.
  • context.WithTimeout(parent Context, timeout time.Duration): Creează un context copil cu un timeout.
  • context.WithValue(parent Context, key, val any): Creează un context copil cu o valoare.

Implementări principale ale Context:

  • emptyCtx: De bază, neanulabil și fără valori. Se folosește Background și TODO.
  • cancelCtx: Suportă anularea. Constă dintr-un context de bază și un canal de anulare.
  • timerCtx: Suportă termen limită/timeout. Conține cancelCtx și un temporizator.
  • valueCtx: Suportă transmiterea valorilor. Conține un context de bază și perechi cheie-valoare.

Când contextul părinte este anulat, toate contexturile copil sunt anulate automat. Acest lucru permite propagarea semnalelor de anulare sau a termenelor limită pe lanțul de apeluri.

Exemplu de utilizare a context.WithCancel:

package main

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

func main() {
	// Crează contextul rădăcină și funcția de anulare
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel() // Asigură anularea la ieșirea din main

	go func() {
		select {
		case <-ctx.Done(): // Așteaptă anularea contextului
			fmt.Println("Sarcina a fost anulată:", ctx.Err())
			return
		case <-time.After(5 * time.Second): // Sau se termină după 5 secunde
			fmt.Println("Sarcina a fost finalizată")
		}
	}()

	// Anulează contextul din altă parte
	time.Sleep(1 * time.Second)
	fmt.Println("Anularea contextului...")
	cancel()

	// Oferă timp goroutine-ului să proceseze anularea
	time.Sleep(100 * time.Millisecond)
}

Exemplu de utilizare a 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("Procesarea cererii pentru User ID:", userID)
	} else {
		fmt.Println("User ID nu a fost găsit în context")
	}
}

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

Contexturile sunt sigure pentru utilizarea în concurență și imuabile, ceea ce este important pentru codul concurent. Ele nu sunt destinate să transmită parametri nesuprafață funcțiilor, ci mai degrabă pentru metadate care influențează execuția procesului.