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
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
selectbloque 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
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
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 :
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
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 :
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
Implémentez une sélection avec deux cas qui reçoivent de ch1 et ch2 ; imprimez lequel est arrivé en premier.
Afficher l'indice
Syntaxe `select { case ...: ... }` avec un cas par canal.
Solution disponible après 3 tentatives
Ajoutez un cas d'expiration avec time.After(time.Second) à la sélection.
Afficher l'indice
`time.After(d)` est un canal qui reçoit après d.
Solution disponible après 3 tentatives
Qu'est-ce qui rend une sélection non bloquante ?
select { case v := <-ch: ... ??? }Récapitulatif
selectattend 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 : utilisezcontext).- Pattern : canal terminé, boucle d'événement, canal nul pour désactiver un cas.
select{}vide = blocage éternel : presque toujours un bug.