Instrukcja select
Część sekcji Programowanie obiektowe ścieżki GO w Coddy. Lekcja 62 z 107.
Instrukcja select pozwala gorutynie jednocześnie czekać na wiele operacji na kanałach. Przypomina instrukcję switch, ale każdy przypadek obejmuje wysłanie do kanału lub odbiór z kanału. Gdy kilka kanałów jest gotowych, select wybiera jeden losowo.
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)
}
}Bez select odbieranie z ch1 blokowałoby działanie do momentu nadejścia danych, co mogłoby spowodować pominięcie danych z ch2. Dzięki select obsługiwany jest kanał, który jako pierwszy otrzyma dane.
Przypadek default sprawia, że select nie blokuje działania. Wykonuje się natychmiast, jeśli żaden kanał nie jest gotowy:
select {
case msg := <-messages:
fmt.Println("Received:", msg)
default:
fmt.Println("No message available")
}Typowy wzorzec łączy select z limitami czasu za pomocą time.After, które zwraca kanał odbierający wartość po określonym czasie:
select {
case result := <-ch:
fmt.Println("Got result:", result)
case <-time.After(2 * time.Second):
fmt.Println("Timeout!")
}Zapobiega to bezterminowemu oczekiwaniu programu, gdy operacja na kanale może nigdy się nie zakończyć.
Wyzwanie
ŁatwyZbudujmy monitor usług, który używa instrukcji select do obsługi wielu współbieżnych źródeł danych. Monitor będzie jednocześnie nasłuchiwać różnych kanałów usług i odpowiednio reagować, gdy nadejdą dane lub gdy nie będą dostępne żadne aktualizacje.
Rozmieścisz swój kod w dwóch plikach:
monitor.go: Zdefiniuj logikę monitorowania usług z obsługą kanałów opartą na select.Utwórz strukturę
ServiceUpdatez polamiName(string) iStatus(string).Zaimplementuj funkcję
Monitor, która przyjmuje trzy kanały:primary chan ServiceUpdate,backup chan ServiceUpdateidone chan bool. Funkcja powinna używaćselect, aby jednocześnie nasłuchiwać na wszystkich trzech kanałach:- Gdy odbierze dane z
primary: zwróć sformatowany tekstPrimary: [Name] is [Status] - Gdy odbierze dane z
backup: zwróć sformatowany tekstBackup: [Name] is [Status] - Gdy odbierze dane z
done: zwróćMonitoring stopped
Zaimplementuj funkcję
CheckStatus, która przyjmuje jeden kanałupdates chan ServiceUpdatei używaselectz przypadkiemdefault, aby wykonać nieblokujące sprawdzenie:- Jeśli aktualizacja jest dostępna: zwróć
Update available: [Name] - Jeśli aktualizacja nie jest dostępna (przypadek default): zwróć
No updates pending
- Gdy odbierze dane z
main.go: Skonfiguruj kanały i pokaż działanie blokującego i nieblokującego select.Odczytaj z wejścia tryb, który określa, który scenariusz uruchomić:
primary,backup,stoplubcheck.W trybach
primary,backupistop: odczytaj także nazwę i status usługi. Utwórz trzy kanały, uruchom gorutynę, która wysyła dane do odpowiedniego kanału na podstawie trybu, a następnie wywołajMonitori wyświetl jego wynik.W trybie
check: odczytaj nazwę i status usługi oraz flagę (sendlubnosend) wskazującą, czy przed sprawdzeniem wysłać dane. Utwórz buforowany kanał o pojemności 1. Jeśli flaga ma wartośćsend, wyślij aktualizację do kanału przed wywołaniemCheckStatus. Wyświetl wynik funkcjiCheckStatus.
Zostaną podane następujące dane wejściowe:
- Linia 1: tryb (
primary,backup,stoplubcheck) - Linia 2: nazwa usługi
- Linia 3: status usługi
- Linia 4 (tylko dla trybu
check):sendlubnosend
Na przykład dla danych:
primary
Database
healthyWynik powinien wyglądać następująco:
Primary: Database is healthyA dla danych:
check
Cache
active
nosendWynik powinien wyglądać następująco:
No updates pendingSpróbuj swoich sił
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: Obsłuż różne tryby: "primary", "backup", "stop", "check"
// Dla trybów "primary", "backup", "stop":
// - Utwórz trzy kanały: primary, backup, done
// - Uruchom gorutynę, która wysyła dane do odpowiedniego kanału
// - Wywołaj Monitor i wypisz wynik
// Dla trybu "check":
// - Odczytaj dodatkową flagę ("send" lub "nosend")
// - Utwórz kanał buforowany o pojemności 1
// - Jeśli flaga to "send", wyślij aktualizację do kanału
// - Wywołaj CheckStatus i wypisz wynik
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy OOP w Go
Pliki zewnętrzneObszar roboczy i moduły GoPakiety i importyNazwy eksportowane i nieeksportowaneWprowadzenie do OOP w GoStruktury jako klasyDefiniowanie metod dla strukturOdbiorniki wskaźnikowe i wartościoweInicjalizacja strukturFunkcje konstruktoraPodsumowanie – prosty kalkulator4Interfejsy
Wprowadzenie do interfejsówImplementacja niejawnaInterfejs jako kontraktPusty interfejs (any)Asercja typuPrzełącznik typówKompozycja interfejsówInterfejsy Stringer i ErrorPowtórka — kalkulator figur7Hermetyzacja
Pola eksportowane i nieeksportowaneHermetyzacja na poziomie pakietuMetody pobierające i ustawiająceUkrywanie informacji w GoPowtórka – dane studentów10Typy generyczne (Go 1.18+)
Wprowadzenie do typów generycznychParametry typówOgraniczenia typówStruktury generyczneObejście ograniczeń metod generycznychPodsumowanie — generyczna kolekcja2Typy i struktury w praktyce
Typy proste i złożoneDefiniowanie własnych typówTagi pól strukturStruktury anonimoweStruktury zagnieżdżoneWartości zerowe i domyślnePodsumowanie – książka kontaktów5Kompozycja zamiast dziedziczenia
Dlaczego Go nie ma dziedziczeniaPodstawy osadzania strukturPromowanie metodOsadzanie wielu strukturOsadzanie a agregacjaPrzesłanianie osadzonych metodPodsumowanie — hierarchia pracowników8Obsługa błędów i programowanie obiektowe
Interfejs błęduWłasne typy błędówOpakowywanie błędów (fmt.Errorf)Błędy wartowniczeerrors.Is() i errors.As()Panic, Defer i RecoverPodsumowanie — parser plików3Wskaźniki i pamięć
Podstawy wskaźników w GoWskaźniki do strukturPrzekazywanie przez wartość a przez referencjęFunkcja new()Odśmiecanie pamięci w GoPowtórka — kreator listy wiązanej6Polimorfizm w Go
Polimorfizm za pomocą interfejsówDuck typing w GoZasady spełniania interfejsówKolekcje polimorficzneWstrzykiwanie zależnościPowtórka — procesor płatności9Współbieżność i OOP
Podstawy goroutinesKanały i komunikacjaKanały buforowane i niebuforowaneInstrukcja selectsync.Mutex i sync.RWMutexsync.WaitGroupProjektowanie struktur bezpiecznych wątkowoPowtórka – pula workerów12Zaawansowane zagadnienia programowania obiektowego
Wzorzec opcji funkcyjnychWzorzec Builder w GoŁańcuchowe wywoływanie metodAliasy typów a definicje typówPodstawy refleksjiPrzegląd generowania koduPoćwicz samodzielnie: Kompilator Go online