Оператор select
Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy. Урок 62 из 107.
Оператор select позволяет горутине одновременно ожидать выполнения нескольких операций с каналами. Он похож на оператор switch, но каждый случай связан с отправкой в канал или получением из канала. Когда несколько каналов готовы, select случайным образом выбирает один из них.
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)
}
}Без select получение из ch1 блокировалось бы до поступления данных, из-за чего данные в ch2 могли бы быть пропущены. С помощью select обрабатывается тот канал, который первым получает данные.
Случай default делает select неблокирующим. Он выполняется немедленно, если ни один канал не готов:
select {
case msg := <-messages:
fmt.Println("Received:", msg)
default:
fmt.Println("No message available")
}Распространённый шаблон объединяет select с тайм-аутами с помощью time.After, который возвращает канал, получающий значение после указанной длительности:
select {
case result := <-ch:
fmt.Println("Got result:", result)
case <-time.After(2 * time.Second):
fmt.Println("Timeout!")
}Это предотвращает бесконечное ожидание вашей программы, когда операция с каналом может никогда не завершиться.
Задание
ЛегкоДавайте создадим монитор службы, который использует оператор select для обработки нескольких параллельных источников данных. Ваш монитор будет одновременно прослушивать разные каналы служб и соответствующим образом реагировать, когда поступают данные или когда обновления недоступны.
Вы организуете код в двух файлах:
monitor.go: Define логику мониторинга службы с обработкой каналов на основе select.Создайте структуру
ServiceUpdateс полямиName(string) иStatus(string).Implement function
Monitor, принимающую три канала:primary chan ServiceUpdate,backup chan ServiceUpdateиdone chan bool. Эта function должна использоватьselectдля одновременного прослушивания всех трёх каналов:- При получении данных из
primary: вернуть форматированную строкуPrimary: [Name] is [Status] - При получении данных из
backup: вернуть форматированную строкуBackup: [Name] is [Status] - При получении данных из
done: вернутьMonitoring stopped
Implement function
CheckStatus, принимающую один каналupdates chan ServiceUpdateи использующуюselectс веткойdefaultдля выполнения неблокирующей проверки:- Если обновление доступно: вернуть
Update available: [Name] - Если обновление недоступно (ветка default): вернуть
No updates pending
- При получении данных из
main.go: настройте каналы и продемонстрируйте блокирующее и неблокирующее поведение select.Считайте из ввода режим, определяющий выполняемый сценарий:
primary,backup,stopилиcheck.Для режимов
primary,backupиstop: также считайте имя и статус службы. Создайте три канала, запустите горутину, которая отправляет данные в соответствующий канал в зависимости от режима, затем вызовитеMonitorи выведите его результат.Для режима
check: считайте имя и статус службы, а также флаг (sendилиnosend), указывающий, нужно ли отправлять данные перед проверкой. Создайте буферизованный канал с capacity 1. Если флаг имеет значениеsend, отправьте обновление в канал перед вызовомCheckStatus. Выведите результатCheckStatus.
Будут предоставлены следующие входные данные:
- Строка 1: режим (
primary,backup,stopилиcheck) - Строка 2: имя службы
- Строка 3: статус службы
- Строка 4 (только для режима
check):sendилиnosend
Например, если даны:
primary
Database
healthyВаш вывод должен быть следующим:
Primary: Database is healthyА если даны:
check
Cache
active
nosendВаш вывод должен быть следующим:
No updates pendingПопробуйте сами
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: Обработать различные режимы: "primary", "backup", "stop", "check"
// Для режимов "primary", "backup", "stop":
// - Создать три канала: primary, backup, done
// - Запустить горутину, которая отправляет данные в соответствующий канал
// - Вызвать Monitor и вывести результат
// Для режима "check":
// - Прочитать дополнительный флаг ("send" или "nosend")
// - Создать буферизованный канал ёмкостью 1
// - Если флаг равен "send", отправить обновление в канал
// - Вызвать CheckStatus и вывести результат
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП в Go
Внешние файлыРабочее пространство и модули GoПакеты и импортыЭкспортируемые и неэкспортируемые именаВведение в ООП в GoСтруктуры как классыОпределение методов структурПолучатели-указатели и получатели-значенияИнициализация структурФункции-конструкторыИтоги — Простой калькулятор4Интерфейсы
Введение в интерфейсыНеявная реализацияИнтерфейс как контрактПустой интерфейс (any)Утверждение типаПереключатель типовКомпозиция интерфейсовИнтерфейсы Stringer и ErrorПовторение: Калькулятор фигур7Инкапсуляция
Экспортируемые и неэкспортируемые поляИнкапсуляция на уровне пакетовГеттеры и сеттерыСокрытие информации в GoИтоги — Записи о студентах10Обобщения (Generics) (Go 1.18+)
Введение в GenericsПараметры типовОграничения типовОбобщенные структурыОбходной путь для обобщенных методовИтоги — Обобщенная коллекция13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн Abstract FactoryПаттерн ObserverПаттерн Strategy2Глубокое погружение в типы и структуры
Базовые и составные типыОпределение пользовательских типовТеги структурАнонимные структурыВложенные структурыНулевые значения и значения по умолчаниюПовторение — Контактная книга5Композиция вместо наследования
Почему в Go нет наследованияОсновы встраивания структурПродвижение методовВстраивание нескольких структурВстраивание против агрегацииЗатенение встроенных методовИтоги — Иерархия сотрудников8Обработка ошибок и ООП
Интерфейс errorПользовательские типы ошибокОбертывание ошибок (fmt.Errorf)Sentinel-ошибкиerrors.Is() и errors.As()Panic, Defer и RecoverИтоги — Парсер файлов11Стандартная библиотека и ООП
io.Reader и io.Writersort.InterfaceИнтерфейс fmt.Stringerencoding/json со структурамиИнтерфейс http.HandlerПовторение: модели REST API14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикMiddleware как Декоратор3Указатели и память
Основы указателей в GoУказатели на структурыПередача по значению и по ссылкеФункция new()Сборка мусора в GoПовторение: Конструктор связного списка6Полиморфизм в Go
Полиморфизм через интерфейсыУтиная типизация в GoПравила реализации интерфейсовПолиморфные коллекцииВнедрение зависимостейИтоги — Обработчик платежей9Конкурентность и ООП
Основы горутинКаналы и взаимодействиеБуферизованные и небуферизованные каналыОператор selectsync.Mutex и sync.RWMutexsync.WaitGroupПроектирование потокобезопасных структурПовторение — Worker PoolПотренируйтесь самостоятельно: Онлайн-компилятор Go