Passer au contenu principal
eLearner.app
Module 7 · Leçon 3 sur 533/50 dans le cours~12 min
Leçons du module (3/5)

select : multiplexage de canaux

select attend des opérations sur plusieurs canaux en même temps. C'est le switch du concours Go : il permet de réagir au premier chaîne prête parmi tant d'autres.

Syntaxe

Go
select {
case v := <-ch1:
    fmt.Println("ch1:", v)
case v := <-ch2:
    fmt.Println("ch2:", v)
case ch3 <- 99:
    fmt.Println("inviato su ch3")
case <-time.After(time.Second):
    fmt.Println("timeout")
default:
    fmt.Println("nessuno è pronto")
}

Sémantique :

  • Toutes les maisons sont évaluées ; un seul des cas « prêts » est choisi.
  • Si plusieurs maisons sont prêtes, le choix est pseudo-aléatoire (pas de commande).
  • Si aucun cas n'est prêt : le select bloque jusqu'à ce qu'un cas soit prêt.
  • S'il y a un default : s'exécute immédiatement lorsqu'aucun dossier n'est prêt (sélectionnez non bloquant).

Délai d'expiration du modèle

Go
select {
case res := <-fetch(url):
    use(res)
case <-time.After(2 * time.Second):
    log.Println("timeout: fetch troppo lento")
}

time.After(d) renvoie un <-chan Time qui produit une valeur après d. C'est la manière classique de limiter l'attente.

Sélectionnez non bloquant par défaut

Go
select {
case msg := <-ch:
    process(msg)
default:
    // niente da fare, prosegui
}

« Sondez » la chaîne de manière vague. Utile dans les files d'attente qui doivent rester réactif sans avoir à attendre.

Modèle "canal terminé"

Combinez select avec un canal d'effacement :

Go
func worker(in <-chan Job, done <-chan struct{}) {
    for {
        select {
        case j := <-in:
            process(j)
        case <-done:
            return       // chi gestisce done segnala lo shutdown
        }
    }
}

close(done) par le coordinateur déclenche le case <-done dans toutes les goroutines en attente : arrêt coordonné.

Boucle avec sélection

Go
for {
    select {
    case v := <-input:
        if v == nil {
            return
        }
        handle(v)
    case <-time.After(5 * time.Second):
        keepAlive()
    case <-ctx.Done():
        return
    }
}

Le modèle de « boucle d'événements » typique des serveurs et des travailleurs.

Canal nul car les maisons sont "éteintes"

Une astuce avancée : un case <-ch avec ch == nil ne vient jamais sélectionné. Permet de « désactiver » dynamiquement un cas :

Go
var in chan int = source()
for {
    select {
    case v, ok := <-in:
        if !ok {
            in = nil    // disabilita questo case
            continue
        }
        handle(v)
    case <-ctx.Done():
        return
    }
}

Lorsque in est fermé, je l'annule : le select continue de gérer seulement les autres maisons.

Essayez-le vous-même

Exercice#go.m7.l3.e1
Tentatives : 0Chargement…

Implémentez une sélection avec deux cas qui reçoivent de ch1 et ch2 ; imprimez lequel est arrivé en premier.

Chargement de l'éditeur…
Afficher l'indice

Syntaxe `select { case ...: ... }` avec un cas par canal.

Solution disponible après 3 tentatives

Exercice#go.m7.l3.e2
Tentatives : 0Chargement…

Ajoutez un cas d'expiration avec time.After(time.Second) à la sélection.

Chargement de l'éditeur…
Afficher l'indice

`time.After(d)` est un canal qui reçoit après d.

Solution disponible après 3 tentatives

Quiz#go.m7.l3.e3
Prêt

Qu'est-ce qui rend une sélection non bloquante ?

Go
select { case v := <-ch: ... ??? }
Options de réponse

Récapitulatif

  • select attend le premier cas prêt parmi plusieurs opérations sur canaux.
  • Choix pseudo-aléatoire entre plusieurs maisons prêtes.
  • default → sélectionnez non bloquant (polling).
  • time.After(d) pour le timeout (attention aux fuites de boucle : utilisez context).
  • Pattern : canal terminé, boucle d'événement, canal nul pour désactiver un cas.
  • select{} vide = blocage éternel : presque toujours un bug.