Реализация таймера golang
Не могу разобраться в работе таймера в golang. Есть условие которое если соблюдено отправляется в другую функцию, которая возвращает либо ошибку, если что-то пошло не так, либо nil если все ок. Если что-то идет не так то ответ идет очень долго (функция дергает инициализацию устройтсва на com-порте), если устройство есть, то инициализация просисходит моментально. Созрел план просто сделать таймер: если инит не прошел быстро, то переставать ожидать ответ от устройства и возвращать nil.
if !condition {
if func() == nil {
condition = true
}
}
Подскажите, пожалуйста как можно это реализовать? Я непонимаю как работают таймеры и горутины. За ранее всем большое спасибо
Ответы (2 шт):
Вы можете использовать функцию time.After() для создания таймера. Эта функция возвращает канал, который закрывается через указанное время. Вы можете использовать конструкцию select для ожидания возврата либо результата функции, либо таймаута.
select {
case res := <- func():
if res == nil {
condition = true
}
case <- time.After(time.Second * 3):
// таймаут, выходим из функции
}
В этом примере функция func() возвращает канал, который закрывается, когда функция завершает работу. Если функция возвращает nil, то условие condition становится true.
В этом примере мы создаем контекст с таймаутом 5 секунд. Затем мы запускаем горутину для инициализации устройства, который иммитирует ваше поведение.
package main
import (
"context"
"fmt"
"time"
)
func main() {
// создаем контекст с таймаутом
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
// создаем горутину для инициализации устройства
var condition bool
initDevice := func() error {
// имитируем длительную инициализацию
time.Sleep(time.Second * 10)
condition = true
return nil
}
go func() {
if !condition {
if initDevice() == nil {
condition = true
}
}
}()
select {
case <-ctx.Done():
fmt.Println("Initialization timeout")
case <-time.After(time.Second * 15):
fmt.Println("Initialization successful")
}
}
Если нужно простое решение, то вот:
var ErrTimeout = errors.New("timeout error")
func WithTimeout(f func() error, timeout time.Duration) error {
ch := make(chan error)
go func() {
ch <- f()
}()
go func() {
time.Sleep(timeout)
ch <- ErrTimeout
}()
return <-ch
}
- Создаём канал с ошибками
- Запускаем первую горутину, которая ждет ответа от функции и сразу записывает ответ в канал
- Запускаем вторую горутину, которая является таймером, по истечении которого в канал записывается ошибка:
"timeout error" - Слушаем наш канал и ответ из него возвращаем из функции
Всё что вам осталось - это обернуть вашу функцию (с инициализацией com-порта) в функцию WithTimeout, указав время ожидания подключения.
Единственный минус в том, что по истечению таймера остаётся висеть первая горутина.