Есть ли способ делать повторяющиеся задачи через определенные промежутки времени?

Есть ли способ сделать повторяющиеся фоновые задачи в Go? Я думаю о чем-то вроде Timer.schedule(task, delay, period) в Java. Я знаю, что могу сделать это с помощью goroutine и Time.sleep(), но мне бы хотелось что-то, что легко остановилось.

Вот что я получил, но выглядит уродливо для меня. Есть ли более чистый/лучший способ?

func oneWay() {
    var f func()
    var t *time.Timer

    f = func () {
        fmt.Println("doing stuff")
        t = time.AfterFunc(time.Duration(5) * time.Second, f)
    }

    t = time.AfterFunc(time.Duration(5) * time.Second, f)

    defer t.Stop()

    //simulate doing stuff
    time.Sleep(time.Minute)
}

Ответ 1

Функция time.NewTicker делает канал, который отправляет периодическое сообщение и предоставляет способ его остановить. Используйте его что-то вроде этого (untested):

ticker := time.NewTicker(5 * time.Second)
quit := make(chan struct{})
go func() {
    for {
       select {
        case <- ticker.C:
            // do stuff
        case <- quit:
            ticker.Stop()
            return
        }
    }
 }()

Вы можете остановить работника, закрыв канал quit: close(quit).

Ответ 2

Как насчет чего-то вроде

package main

import (
    "fmt"
    "time"
)

func schedule(what func(), delay time.Duration) chan bool {
    stop := make(chan bool)

    go func() {
        for {
            what()
            select {
            case <-time.After(delay):
            case <-stop:
                return
            }
        }
    }()

    return stop
}

func main() {
    ping := func() { fmt.Println("#") }

    stop := schedule(ping, 5*time.Millisecond)
    time.Sleep(25 * time.Millisecond)
    stop <- true
    time.Sleep(25 * time.Millisecond)

    fmt.Println("Done")
}

Игровая площадка

Ответ 3

Если вас не интересует смещение тиков (в зависимости от того, сколько времени это занимало ранее при каждом выполнении) и вы не хотите использовать каналы, можно использовать встроенную функцию диапазона.

т.е.

package main

import "fmt"
import "time"

func main() {
    go heartBeat()
    time.Sleep(time.Second * 5)
}

func heartBeat() {
    for range time.Tick(time.Second * 1) {
        fmt.Println("Foo")
    }
}

Детская площадка

Ответ 4

Посмотрите эту библиотеку: https://github.com/robfig/cron

Пример, как показано ниже:

c := cron.New()
c.AddFunc("0 30 * * * *", func() { fmt.Println("Every hour on the half hour") })
c.AddFunc("@hourly",      func() { fmt.Println("Every hour") })
c.AddFunc("@every 1h30m", func() { fmt.Println("Every hour thirty") })
c.Start()

Ответ 5

Более широкий ответ на этот вопрос мог бы рассмотреть подход к кирпичу Lego, который часто используется в Оккаме, и предлагаемый сообществу Java через JCSP. По этой идее есть очень хорошая презентация Peter Welch.

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

Итак, когда дело доходит до разработки повторяющихся задач, вы можете создать свою систему как сеть потоков данных из простых компонентов (как goroutines), которые обмениваются событиями (то есть сообщениями или сигналами) через каналы.

Этот подход является композиционным: каждая группа малых компонентов может сама себя вести как более крупная составляющая, бесконечность. Это может быть очень мощным, потому что сложные параллельные системы сделаны из легко понятных кирпичей.

Сноска: в презентации Welch он использует синтаксис Оккама для каналов, который ! и ?, и они непосредственно соответствуют ch < - и < -ch в Go.

Ответ 6

Я использую следующий код:

package main

import (
    "fmt"
    "time"
)

func main() {
    now := time.Now()
    fmt.Println("\nToday:", now)

    after := now.Add(1 * time.Minute)
    fmt.Println("\nAdd 1 Minute:", after)

    for {
        fmt.Println("test")
        time.Sleep(10 * time.Second)

        now = time.Now()

        if now.After(after) {
            break
        }
    }

    fmt.Println("done")
}

Это более просто и прекрасно работает для меня.

Ответ 7

Если вы хотите, чтобы остановить его в любой момент тикер

ticker := time.NewTicker(500 * time.Millisecond)
go func() {
    for range ticker.C {
        fmt.Println("Tick")
    }
}()
time.Sleep(1600 * time.Millisecond)
ticker.Stop()

Если вы не хотите, чтобы остановить это, отметьте:

tick := time.Tick(500 * time.Millisecond)
for range tick {
    fmt.Println("Tick")
}