Ответ
Это классическая задача для select
в цикле for
. Горутина должна выполнять периодические действия (по тикеру) и одновременно реагировать на входящие сообщения или сигналы завершения.
Неправильный подход — использовать select
с default
и time.Sleep
. Это создает неэффективный цикл (busy-wait), который постоянно потребляет CPU, просыпаясь и засыпая.
Правильное решение — использовать блокирующий select
для ожидания нескольких каналов. Он будет "спать" и не потреблять ресурсы до тех пор, пока в одном из каналов не появится событие.
Вот канонический пример реализации рабочего цикла (worker):
package main
import (
"fmt"
"time"
)
func worker(msgChan <-chan string, doneChan <-chan struct{}) {
// Создаем тикер, который будет срабатывать каждые 2 секунды
ticker := time.NewTicker(2 * time.Second)
// Важно остановить тикер, чтобы избежать утечки ресурсов
defer ticker.Stop()
for {
select {
// Случай 1: Пришло сообщение для обработки
case msg := <-msgChan:
fmt.Printf("Получено сообщение: %sn", msg)
// Случай 2: Сработал тикер для периодической задачи
case t := <-ticker.C:
fmt.Printf("Тик в %vn", t.Format(time.Kitchen))
// Случай 3: Пришел сигнал о завершении работы
case <-doneChan:
fmt.Println("Воркер завершает работу...")
return
}
}
}
func main() {
msgChan := make(chan string, 1)
doneChan := make(chan struct{})
go worker(msgChan, doneChan)
// Отправляем сообщения для обработки
msgChan <- "Первое задание"
time.Sleep(3 * time.Second)
msgChan <- "Второе задание"
// Ждем 5 секунд и завершаем воркер
time.Sleep(5 * time.Second)
close(doneChan)
// Даем время на graceful shutdown
time.Sleep(1 * time.Second)
fmt.Println("Программа завершена")
}
Ключевые моменты этого паттерна:
for-select
: Бесконечный цикл сselect
внутри — основа для долгоживущих горутин.- Блокировка:
select
блокирует горутину до тех пор, пока один изcase
не станет доступен для выполнения (чтения или записи). - Graceful Shutdown: Наличие канала
doneChan
позволяет извне безопасно остановить горутину, давая ей возможность завершить текущие операции и освободить ресурсы. defer ticker.Stop()
: Тикеры — это ресурсы, которые нужно освобождать, иначе они будут работать до конца жизни программы, даже если на них никто не слушает.