Canaux et communication
Fait partie de la section Programmation Orientée Objet du Journey GO de Coddy. Leçon 60 sur 107.
Les canaux sont le principal mécanisme de Go pour la communication entre les goroutines. Plutôt que de partager directement la mémoire, les goroutines envoient et reçoivent des valeurs par l’intermédiaire des canaux, conformément à la philosophie de Go : « Ne communiquez pas en partageant la mémoire ; partagez la mémoire en communiquant. »
Créez un canal à l’aide de la fonction make avec le mot-clé chan :
messages := make(chan string)Utilisez l'opérateur flèche <- pour envoyer et recevoir des valeurs. La flèche indique la direction du flux de données :
func main() {
messages := make(chan string)
go func() {
messages <- "hello" // envoyer au canal
}()
msg := <-messages // recevoir du canal
fmt.Println(msg) // Sortie : hello
}Par défaut, les envois et les réceptions sont bloquants jusqu’à ce que l’autre partie soit prête. Lorsque la goroutine envoie "hello", elle attend que main soit prête à recevoir. Ce comportement bloquant fournit une synchronisation intégrée : aucun time.Sleep n’est nécessaire.
Vous pouvez fermer un canal pour signaler qu’aucune autre valeur ne sera envoyée. Les récepteurs peuvent le détecter :
close(messages)
msg, ok := <-messages
if !ok {
fmt.Println("Channel closed")
}Les canaux sont typés : un chan string ne peut transporter que des chaînes. Cette sécurité de typage garantit que les goroutines communiquent avec les types de données appropriés, en détectant les erreurs au moment de la compilation plutôt qu’à l’exécution.
Défi
FacileConstruisons un système de relais de messages qui montre comment les goroutines communiquent via des canaux. Vous allez créer un pipeline dans lequel les messages circulent entre différentes étapes de traitement, chaque étape s’exécutant simultanément.
Vous organiserez votre code sur deux fichiers :
relay.go: Définissez votre pipeline de traitement des messages.Créez une structure
Messageavec les champsID(int) etContent(string).Implémentez trois fonctions qui représentent différentes étapes de votre pipeline :
Producer(messages []Message, out chan Message)- Reçoit une tranche de messages et envoie chacun d’eux au canal de sortie. Après avoir envoyé tous les messages, fermez le canal pour signaler la fin du traitement.Transformer(in chan Message, out chan Message)- Reçoit les messages du canal d’entrée, transforme le contenu de chaque message en majuscules, puis envoie le message transformé au canal de sortie. Lorsque le canal d’entrée est fermé (ce qui est détecté à l’aide de la forme de réception à deux valeurs), fermez le canal de sortie.Consumer(in chan Message) []string- Reçoit tous les messages du canal d’entrée et les rassemble dans une tranche de chaînes formatées. Chaque chaîne doit respecter le format :Message [ID]: [Content]. Retournez la tranche lorsque le canal est fermé.
main.go: Configurez le pipeline et orchestrez le flux de messages simultané.Lisez le nombre de messages, puis lisez l’identifiant et le contenu de chaque message. Créez les messages et configurez deux canaux pour relier les trois étapes du pipeline. Lancez Producer et Transformer en tant que goroutines, puis exécutez Consumer dans la goroutine principale afin de recueillir les résultats. Affichez chaque résultat sur une ligne distincte.
Les entrées suivantes seront fournies :
- Ligne 1 : nombre de messages (entier)
- Lignes suivantes : pour chaque message, deux lignes : l’identifiant du message (entier), puis son contenu (chaîne)
Par exemple, avec :
2
1
hello world
2
go channelsVotre sortie doit être :
Message 1: HELLO WORLD
Message 2: GO CHANNELSLe pipeline fonctionne ainsi : Producer envoie les messages → Transformer les convertit en majuscules → Consumer rassemble et formate les résultats. Chaque flèche représente un canal reliant des étapes simultanées. Utilisez le package strings pour la transformation en majuscules.
Essayez vous-même
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Lire le nombre de messages
nLine, _ := reader.ReadString('\n')
n, _ := strconv.Atoi(strings.TrimSpace(nLine))
// Lire les messages
messages := make([]Message, n)
for i := 0; i < n; i++ {
idLine, _ := reader.ReadString('\n')
id, _ := strconv.Atoi(strings.TrimSpace(idLine))
contentLine, _ := reader.ReadString('\n')
content := strings.TrimSpace(contentLine)
messages[i] = Message{ID: id, Content: content}
}
// TODO: Créer deux canaux pour connecter les étapes du pipeline
// TODO: Lancer Producer en tant que goroutine
// TODO: Lancer Transformer en tant que goroutine
// TODO: Exécuter Consumer dans la goroutine principale et collecter les résultats
// TODO: Afficher chaque résultat sur une ligne séparée
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
1Fondamentaux de la POO en Go
Fichiers externesEspace de travail et modules GoPackages et importsNoms exportés et non exportésIntroduction à la POO en GoStructs comme classesDéfinir des méthodes sur des structsRécepteurs pointeurs ou par valeurInitialisation des structsFonctions constructeursRécapitulatif - Calculatrice simple4Interfaces
Introduction aux interfacesImplémentation impliciteL’interface comme contratInterface vide (any)Assertion de typeCommutation de typeComposition d’interfacesInterfaces Stringer et ErrorRécapitulatif - Calculateur de formes7Encapsulation
Champs exportés vs non exportésEncapsulation au niveau du packageMéthodes Getter et SetterDissimulation de l’information en GoRécapitulatif – Fiches d’étudiants10Génériques (Go 1.18+)
Introduction aux génériquesParamètres de typeContraintes de typeStructures génériquesSolution de contournement pour les méthodes génériquesRécapitulatif - Collection générique2Plongée approfondie dans les types et les structs
Types de base et compositesDéfinitions de types personnalisésTags de structStructs anonymesStructs imbriquésValeurs zéro et valeurs par défautRécapitulatif - Carnet de contacts5La composition plutôt que l’héritage
Pourquoi Go n’a pas d’héritageBases de l’inclusion de structsPromotion des méthodesInclure plusieurs structsInclusion ou agrégationMasquage des méthodes inclusesRécapitulatif : hiérarchie des employés8Gestion des erreurs et POO
L’interface errorTypes d’erreurs personnalisésEnrobage des erreurs (fmt.Errorf)Erreurs sentinelleserrors.Is() et errors.As()Panic, Defer et RecoverRécapitulatif - Analyseur de fichiers11Bibliothèque standard et POO
io.Reader et io.Writersort.InterfaceInterface fmt.Stringerencoding/json avec des structsInterface http.HandlerRécapitulatif - Modèles d’API REST14Modèles de conception – Partie 2
Modèle CommandeModèle AdaptateurModèle DécorateurModèle Méthode modèleModèle ÉtatModèle CompositeMiddleware comme décorateur3Pointeurs et mémoire
Notions de base des pointeurs en GoPointeurs vers des structuresPassage par valeur ou par référenceLa fonction new()Garbage collection en GoRécapitulatif - Constructeur de listes chaînées6Polymorphisme en Go
Polymorphisme via les interfacesDuck typing en GoRègles de satisfaction des interfacesCollections polymorphesInjection de dépendancesRécapitulatif – Processeur de paiements9Concurrence et POO
Bases des GoroutinesCanaux et communicationCanaux tamponnés ou non tamponnésInstruction selectsync.Mutex et sync.RWMutexsync.WaitGroupConception de structures thread-safeRécapitulatif - Pool de workersEntraînez-vous par vous-même : Compilateur Go en ligne