Lektionen des Moduls (2/5)
Kanäle: typisierte Kommunikation
Kanäle sind der idiomatische Mechanismus für die Kommunikation zwischen Goroutinen. Die Go-Philosophie (Rob Pike):
„Kommunizieren Sie nicht, indem Sie Erinnerungen teilen; teilen Sie Erinnerungen, indem Sie kommunizieren.“
Ein Kanal ist eine typisierte FIFO-Warteschlange, typsicher und gleichzeitig sicher
verwenden. In Kombination mit go ist es der Hauptbaustein von Go
Parallelität.
Erstellen, senden, empfangen
ch := make(chan int) // canale di int, unbuffered
go func() {
ch <- 42 // invia (può bloccare)
}()
v := <-ch // ricevi (può bloccare)
fmt.Println(v) // 42Syntax:
make(chan T)odermake(chan T, N)für gepuffert.ch <- vsendet.v := <-chempfängt.v, ok := <-chempfängt mit einem Flag (ok = false, wenn der Kanal geschlossen UND leer ist).
Ungepuffert: Rendezvous
Ein ungepufferter Kanal (Kapazität 0) blockiert:
- der Sender, bis ein Empfänger bereit ist;
- der Empfänger, bis ein Absender bereit ist.
Es handelt sich um einen synchronen Termin: nützlich als „Signal“ oder als Garantie dass der Wert geliefert wurde.
Gepuffert: Warteschlange bis N
ch := make(chan int, 3)
ch <- 1 // non blocca
ch <- 2 // non blocca
ch <- 3 // non blocca
ch <- 4 // BLOCCA: il buffer è pienoDer Absender blockiert nur, wenn der Puffer voll ist; nur der Empfänger wenn es leer ist.
nah und aus der Distanz
Der Absender schließt den Kanal, wenn keine Werte mehr vorhanden sind:
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
close(ch)
for v := range ch { // termina quando ch è chiuso E vuoto
fmt.Println(v)
}for range iteriert über einen Kanal, bis der Kanal geschlossen (und geleert) wird.
Es ist das sauberste Muster für „Alles verbrauchen, was ankommt“.
Direktionalität in Signaturen
Funktionsparameter können die Richtung einschränken:
func producer(out chan<- int) { // solo invio
out <- 1
close(out)
}
func consumer(in <-chan int) { // solo ricezione
for v := range in {
fmt.Println(v)
}
}chan<- T = Nur Senden, <-chan T = Nur Empfangen. Eine bidirektionale
Kanal chan T kann an beide übergeben werden (implizite Konvertierung). Es
verbessert die Übersichtlichkeit und beugt Fehlern vor.
Häufige Muster (Vorschau)
- Worker-Pool: N Goroutinen verbrauchen von
jobs <-chan T, produzieren aufresults chan<- R. - Pipeline: Stufen, die durch Kanäle verbunden sind.
- Fertigsignal: ein
chan struct{}-Kanal geschlossen, um „Stopp“ zu signalisieren.
done := make(chan struct{})
go func() {
<-done // blocca finché qualcuno chiude done
cleanup()
}()
close(done) // segnale di stopchan struct{} ist der „Signal“-Kanal: nur null Byte Nutzlast
Synchronisation.
Probieren Sie es aus
Erstellen Sie einen ungepufferten int-Kanal ch, senden Sie 42 von einer Goroutine, empfangen Sie ihn in main und drucken Sie ihn aus.
Hinweis anzeigen
Ohne Puffer synchronisieren sich Sender und Empfänger.
Lösung nach 3 Versuchen verfügbar
Senden Sie 1,2,3 über einen gepufferten Kanal, schließen Sie ihn und drucken Sie dann alles mit for-range aus.
Hinweis anzeigen
`for range` über einen Kanal endet, wenn der Kanal geschlossen und geleert wird.
Lösung nach 3 Versuchen verfügbar
Was macht ein UNBUFFERED-Kanal, wenn der Sender sendet, aber kein Empfänger bereit ist?
ch := make(chan int)
ch <- 1 // ?Zusammenfassung
- Kanal = typisierte FIFO-Warteschlange, sicher zwischen Goroutinen.
- Ungepuffert: Rendezvous (Sender und Empfänger synchronisieren).
- Gepuffert: Entkopplung, blockiert nur bei Voll/Leer.
close(ch): nur der Absender;for rangeverbraucht bis zum Schließen.- Direktionalität:
chan<- T(nur Senden),<-chan T(nur Empfangen). chan struct{}als Signalkanal für reine Synchronisation.