Реализация таймера golang

Не могу разобраться в работе таймера в golang. Есть условие которое если соблюдено отправляется в другую функцию, которая возвращает либо ошибку, если что-то пошло не так, либо nil если все ок. Если что-то идет не так то ответ идет очень долго (функция дергает инициализацию устройтсва на com-порте), если устройство есть, то инициализация просисходит моментально. Созрел план просто сделать таймер: если инит не прошел быстро, то переставать ожидать ответ от устройства и возвращать nil.

if !condition {
    if func() == nil {
        condition = true
    }
}

Подскажите, пожалуйста как можно это реализовать? Я непонимаю как работают таймеры и горутины. За ранее всем большое спасибо


Ответы (2 шт):

Автор решения: Senior Pomidor

Вы можете использовать функцию 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
}
  1. Создаём канал с ошибками
  2. Запускаем первую горутину, которая ждет ответа от функции и сразу записывает ответ в канал
  3. Запускаем вторую горутину, которая является таймером, по истечении которого в канал записывается ошибка: "timeout error"
  4. Слушаем наш канал и ответ из него возвращаем из функции

Всё что вам осталось - это обернуть вашу функцию (с инициализацией com-порта) в функцию WithTimeout, указав время ожидания подключения. Единственный минус в том, что по истечению таймера остаётся висеть первая горутина.

→ Ссылка