Select-Anweisung
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy — Lektion 62 von 107.
Die select-Anweisung lässt eine Goroutine gleichzeitig auf mehrere Channel-Operationen warten. Sie ist wie eine switch-Anweisung, aber jeder Case beinhaltet ein Senden oder Empfangen über einen Channel. Wenn mehrere Channel bereit sind, wählt select einen zufällig aus.
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
go func() { ch1 <- "from channel 1" }()
go func() { ch2 <- "from channel 2" }()
select {
case msg1 := <-ch1:
fmt.Println(msg1)
case msg2 := <-ch2:
fmt.Println(msg2)
}
}Ohne select würde das Empfangen von ch1 blockieren, bis Daten ankommen, wodurch potenziell Daten auf ch2 verpasst werden. Mit select wird der Kanal zuerst verarbeitet, der zuerst Daten empfängt.
Der default-Fall macht select nicht-blockierend – er wird sofort ausgeführt, wenn keine Kanäle bereit sind:
select {
case msg := <-messages:
fmt.Println("Received:", msg)
default:
fmt.Println("No message available")
}Ein gängiges Muster kombiniert select mit Timeouts unter Verwendung von time.After, das einen Channel zurückgibt, der nach der angegebenen Dauer einen Wert empfängt:
select {
case result := <-ch:
fmt.Println("Got result:", result)
case <-time.After(2 * time.Second):
fmt.Println("Timeout!")
}Dies verhindert, dass Ihr Programm unendlich lange wartet, wenn eine Channel-Operation möglicherweise nie abgeschlossen wird.
Aufgabe
EinfachLassen Sie uns einen Service-Monitor bauen, der die select-Anweisung verwendet, um mehrere gleichzeitige Datenquellen zu verarbeiten. Ihr Monitor wird gleichzeitig auf verschiedene Service-Kanäle hören und angemessen reagieren, wenn Daten ankommen oder wenn keine Updates verfügbar sind.
Sie werden Ihren Code auf zwei Dateien verteilen:
monitor.go: Definieren Sie Ihre Service-Monitoring-Logik mit select-basierter Kanalverarbeitung.Erstellen Sie ein
ServiceUpdate-Struct mit den FeldernName(string) undStatus(string).Implementieren Sie eine
Monitor-Funktion, die drei Kanäle entgegennimmt:primary chan ServiceUpdate,backup chan ServiceUpdateunddone chan bool. Diese Funktion sollteselectverwenden, um gleichzeitig auf allen drei Kanälen zu hören:- Beim Empfang von
primary: Geben Sie den formatierten StringPrimary: [Name] is [Status]zurück. - Beim Empfang von
backup: Geben Sie den formatierten StringBackup: [Name] is [Status]zurück. - Beim Empfang von
done: Geben SieMonitoring stoppedzurück.
Implementieren Sie eine
CheckStatus-Funktion, die einen einzelnenupdates chan ServiceUpdateentgegennimmt undselectmit einemdefault-Fall verwendet, um eine nicht-blockierende Prüfung durchzuführen:- Wenn ein Update verfügbar ist: Geben Sie
Update available: [Name]zurück. - Wenn kein Update verfügbar ist (default-Fall): Geben Sie
No updates pendingzurück.
- Beim Empfang von
main.go: Richten Sie die Kanäle ein und demonstrieren Sie sowohl blockierendes als auch nicht-blockierendes select-Verhalten.Lesen Sie einen Modus aus der Eingabe, der bestimmt, welches Szenario ausgeführt werden soll:
primary,backup,stopodercheck.Für die Modi
primary,backupundstop: Lesen Sie zusätzlich einen Service-Namen und einen Status ein. Erstellen Sie die drei Kanäle, starten Sie eine Goroutine, die basierend auf dem Modus Daten an den entsprechenden Kanal sendet, rufen Sie dannMonitorauf und geben Sie das Ergebnis aus.Für den Modus
check: Lesen Sie einen Service-Namen und einen Status ein, plus ein Flag (sendodernosend), das angibt, ob vor der Prüfung Daten gesendet werden sollen. Erstellen Sie einen gepufferten Kanal mit der Kapazität 1. Wenn das Flagsendist, senden Sie ein Update an den Kanal, bevor SieCheckStatusaufrufen. Geben Sie das Ergebnis vonCheckStatusaus.
Die folgenden Eingaben werden bereitgestellt:
- Zeile 1: Modus (
primary,backup,stopodercheck) - Zeile 2: Service-Name
- Zeile 3: Service-Status
- Zeile 4 (nur für den Modus
check):sendodernosend
Zum Beispiel, gegeben:
primary
Database
healthySollte Ihre Ausgabe wie folgt aussehen:
Primary: Database is healthyUnd gegeben:
check
Cache
active
nosendSollte Ihre Ausgabe wie folgt aussehen:
No updates pendingSpickzettel
Die select-Anweisung ermöglicht es einer Goroutine, gleichzeitig auf mehrere Channel-Operationen zu warten. Sie funktioniert wie eine switch-Anweisung, aber jeder Case beinhaltet das Senden oder Empfangen über einen Channel:
select {
case msg1 := <-ch1:
fmt.Println(msg1)
case msg2 := <-ch2:
fmt.Println(msg2)
}Wenn mehrere Channels bereit sind, wählt select einen zufällig aus. Ohne select würde das Empfangen von einem Channel blockieren und potenziell Daten auf einem anderen Channel verpassen.
Der default-Case macht select nicht-blockierend – er wird sofort ausgeführt, wenn keine Channels bereit sind:
select {
case msg := <-messages:
fmt.Println("Received:", msg)
default:
fmt.Println("No message available")
}Ein gängiges Muster kombiniert select mit Timeouts unter Verwendung von time.After, was einen Channel zurückgibt, der nach der angegebenen Dauer einen Wert empfängt:
select {
case result := <-ch:
fmt.Println("Got result:", result)
case <-time.After(2 * time.Second):
fmt.Println("Timeout!")
}Dies verhindert, dass Ihr Programm ewig wartet, wenn eine Channel-Operation möglicherweise nie abgeschlossen wird.
Probier es selbst
package main
import (
"fmt"
)
func main() {
var mode string
fmt.Scanln(&mode)
var serviceName string
fmt.Scanln(&serviceName)
var serviceStatus string
fmt.Scanln(&serviceStatus)
// TODO: Verschiedene Modi verarbeiten: "primary", "backup", "stop", "check"
// Für die Modi "primary", "backup", "stop":
// - Drei Kanäle erstellen: primary, backup, done
// - Eine Goroutine starten, die Daten an den entsprechenden Kanal sendet
// - Monitor aufrufen und das Ergebnis ausgeben
// Für den Modus "check":
// - Das zusätzliche Flag lesen ("send" oder "nosend")
// - Einen gepufferten Kanal mit Kapazität 1 erstellen
// - Wenn das Flag "send" ist, ein Update an den Kanal senden
// - CheckStatus aufrufen und das Ergebnis ausgeben
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der Go OOP
Externe DateienGo Workspace & ModulePackages & ImportsExportierte vs. nicht-exportierte NamenEinführung in OOP in GoStructs als KlassenMethoden für Structs definierenPointer- vs. Value-ReceiverStruct-InitialisierungKonstruktor-FunktionenZusammenfassung – Einfacher Taschenrechner4Interfaces
Einführung in InterfacesImplizite ImplementierungInterface als VertragLeeres Interface (any)Type AssertionType SwitchInterface-KompositionStringer & Error InterfacesRückblick - Formen-Rechner7Kapselung
Exportierte vs. nicht exportierte FelderKapselung auf Paket-EbeneGetter- & Setter-MethodenInformation Hiding in GoZusammenfassung - Studierendendatensätze10Generics (Go 1.18+)
Einführung in GenericsTypparameterType ConstraintsGenerische StructsWorkaround für generische MethodenZusammenfassung – Generische Collection2Vertiefung: Typen & Structs
Basis- & zusammengesetzte TypenBenutzerdefinierte TypdefinitionenStruct TagsAnonyme StructsVerschachtelte StructsZero Values & StandardwerteZusammenfassung - Kontaktbuch5Komposition statt Vererbung
Warum Go keine Vererbung hatGrundlagen des Struct EmbeddingMethod PromotionEinbetten mehrerer StructsEmbedding vs. AggregationShadowing eingebetteter MethodenZusammenfassung – Mitarbeiter-Hierarchie8Fehlerbehandlung & OOP
Das error-InterfaceEigene FehlertypenError Wrapping (fmt.Errorf)Sentinel-Fehlererrors.Is() und errors.As()Panic, Defer und RecoverZusammenfassung – Datei-Parser3Zeiger & Speicher
Zeiger-Grundlagen in GoZeiger auf StructsWertübergabe vs. ReferenzübergabeDie new() FunktionGarbage Collection in GoWiederholung – Linked List Builder6Polymorphismus in Go
Polymorphismus über InterfacesDuck Typing in GoInterface-ErfüllungsregelnPolymorphe CollectionsDependency InjectionZusammenfassung – Payment Processor9Nebenläufigkeit & OOP
Goroutines GrundlagenChannels & KommunikationGepufferte vs. ungepufferte ChannelsSelect-Anweisungsync.Mutex & sync.RWMutexsync.WaitGroupThread-sicheres Struct-DesignZusammenfassung - Worker Pool