Голанг: Прерывание бесконечного опроса, имеющего время. Сон
Я использую следующий простой механизм опроса:
func poll() {
for {
if a {
device1()
time.Sleep(time.Second * 10)
} else {
sensor1()
time.Sleep(time.Second * 10)
}
}
}
Мне нужно опросить device1, только если "а" истинно, иначе опросить sensor1. Теперь здесь "а" будет установлен в "истина", нажав кнопку на пользовательском интерфейсе, что будет случайным действием.
Но из-за времени. Сон, задержка вводится при проверке состояния.
Есть ли способ, чтобы немедленно остановить время. Сон, когда я получаю значение для "а"? Каковы возможные способы реализации таких прерываний при опросе в Голанге?
1 ответ
Вы не можете прервать time.Sleep()
,
Вместо этого, если вам нужно слушать другие "события" во время ожидания чего-либо, вы должны использовать каналы и select
заявления. select
похож на switch
, но с делами все относящиеся к операциям связи.
Хорошая вещь select
является то, что вы можете перечислить несколько случаев, все ссылки на комм. ops (например, канал получает или отправляет), и любая операция может продолжаться (будет выбран случайным образом, если несколько могут продолжаться). Если никто не может продолжить, он будет блокировать (ждать), пока один из комм. операции могут продолжаться (если нет default
).
Событие "тайм-аут" может быть реализовано с использованием time.After()
или серию "непрерывных" событий тайм-аута (тиковых или тактовых импульсов) с использованием time.Ticker
,
Вы должны изменить свой обработчик кнопки для отправки значения (состояния кнопки) по каналу, чтобы получение от этого канала можно было добавить к select
внутри poll()
и обрабатывается "немедленно" (без ожидания тайм-аута или следующего события тика).
Смотрите этот пример:
func poll(buttonCh <-chan bool) {
isDevice := false
read := func() {
if isDevice {
device1()
} else {
sensor1()
}
}
ticker := time.NewTicker(1 * time.Second)
defer func() { ticker.Stop() }()
for {
select {
case isDevice = <-buttonCh:
case <-ticker.C:
read()
}
}
}
func device1() { log.Println("reading device1") }
func sensor1() { log.Println("reading sensor1") }
Тестирование это:
buttonCh := make(chan bool)
// simulate a click after 2.5 seconds:
go func() {
time.Sleep(2500 * time.Millisecond)
buttonCh <- true
}()
go poll(buttonCh)
time.Sleep(5500 * time.Millisecond)
Вывод (попробуйте на Go Playground):
2009/11/10 23:00:01 reading sensor1
2009/11/10 23:00:02 reading sensor1
2009/11/10 23:00:03 reading device1
2009/11/10 23:00:04 reading device1
2009/11/10 23:00:05 reading device1
Это решение позволяет легко добавлять любое количество источников событий, ничего не меняя. Например, если вы хотите добавить событие "shutdown" и "read-now" в вышеприведенное решение, это будет выглядеть так:
for {
select {
case isDevice = <-buttonCh:
case <-ticker.C:
read()
case <-readNowCh:
read()
case <-shutdownCh:
return
}
}
куда shutdownCh
является каналом с любым типом элемента, и чтобы сигнализировать об отключении, вы делаете это, закрывая его:
var shutdownCh = make(chan struct{})
// Somewhere in your app:
close(shutdownCh)
Закрытие канала имеет то преимущество, что у вас может быть любое количество подпрограмм, "контролирующих" его, все получат отключение (это похоже на трансляцию). Прием из закрытого канала может продолжаться немедленно, давая нулевое значение типа элемента канала.
Чтобы выполнить действие "немедленное чтение", вы должны отправить значение на readNowCh
,