Каков наилучший способ периодически вызывать N параллельных функций в Go? - PullRequest
4 голосов
/ 13 июня 2019

Я боролся с проблемой в течение последнего дня или около того в поиске лучшего способа создания N одновременных функций, которые периодически вызываются с одинаковым интервалом в Go. Я хочу иметь возможность указывать произвольное количество функций, периодически запускать их все одновременно и завершать все через определенное время.

Сейчас у меня есть решение, которое работает, но для каждой одновременной функции необходимо создать новый тикер. Я также не уверен, как правильно использовать sync.WaitGroup, так как моя текущая реализация приводит к тому, что программа никогда не заканчивается (просто застревает в конце wg.Wait ())

Я кратко посмотрел на оболочку тикера под названием Multitick , но я не уверен, как это реализовать. Может быть, Multitick может быть решением здесь?

func main() {
    N := 10

    var wg sync.WaitGroup
    wg.Add(N)

    quit := make(chan struct{})

    for i := 0; i < N; i++ {

        tick := time.NewTicker(500 * time.Millisecond)
        go func(t *time.Ticker) {

            for a := range tick.C {

                select {
                case <-quit:
                    break
                default:
                    fmt.Println(a) // do something on tick
                }
            }
            wg.Done()
        }(tick)
    }

    time.Sleep(10 * time.Second)
    close(quit)
    wg.Wait()
}

Демонстрация Go Playground

Таким образом, это решение работает, выполняя все тикеры одновременно с надлежащими интервалами и заканчивая через 10 секунд, но на самом деле оно не выходит из программы, зависая в конце строки wg.Wait (). Кроме того, каждый параллельный вызов функции использует свой собственный тикер. Можно ли как-нибудь использовать один «главный» тикер, с которого работают все функции?

Заранее спасибо! Это мой первый раз, когда я действительно углубляюсь в параллелизм в Go.

1 Ответ

5 голосов
/ 14 июня 2019

Причиной того, что ваша программа никогда не завершается, является странная причуда языка Go: оператор break для case <-quit выходит из оператора select вместо цикла.(Не уверен, почему это поведение когда-либо было бы полезно.) Чтобы исправить вашу программу, вам нужно явно разорвать цикл:

tickLoop:
    for a := range tick.C {
        select {
        case <-quit:
            break tickLoop
        default:
            fmt.Println(a, "function #", id) // do something on tick
        }
    }

Когда код написан, он всегда ждет следующего такта, прежде чем завершить работу.Вы можете исправить это, прочитав tick.C в операторе выбора:

tickLoop:
    for {
        select {
        case <-quit:
            break tickLoop
        case a := <-tick.C:
            fmt.Println(a, "function #", id) // do something on tick
        }
    }

Наконец, если вы хотите реструктурировать свою программу, чтобы использовать только один тикер, вы можете запустить дополнительную программу, которая прослушиваетна тикере и на канале quit, а затем запускает подпрограммы N на каждом тике.

...