Menu
Coddy logo textTech

Оператор 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!")
}

Это предотвращает бесконечное ожидание вашей программы, когда операция с каналом может никогда не завершиться.

challenge icon

Задание

Легко

Давайте создадим монитор службы, который использует оператор 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 и вывести результат
	
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор Go