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

Рейтинг: 0Ответов: 2Опубликовано: 23.01.2023

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

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

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

Ответы

▲ 0

Если нужно простое решение, то вот:

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, указав время ожидания подключения. Единственный минус в том, что по истечению таймера остаётся висеть первая горутина.

▲ -1

Вы можете использовать функцию 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")
    }
}