2013-05-09 15 views
95

Esiste un modo per eseguire attività di background ripetitive in Go? Sto pensando a qualcosa come Timer.schedule(task, delay, period) in Java. So che posso farlo con una goroutine e Time.sleep(), ma mi piacerebbe qualcosa che si fermasse facilmente.C'è un modo per eseguire attività ripetitive a intervalli in Golang?

Ecco cosa ho ottenuto, ma mi sembra brutto. C'è un modo più pulito/migliore?

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) 
} 
+2

Grazie per aver utilizzato time.Duration (x) nel tuo esempio. Ogni esempio che ho trovato ha un int hardcoded e si lamenta quando usi un v (int) o float. –

+0

@MikeGraf puoi fare 't: = time.Tick (time.Duration (period) * time.Secondo) 'dove periodo è un' int' – florianrosenberg

+0

questa soluzione sembra abbastanza buona, IMO. esp. se si chiama semplicemente f() invece del tempo esterno. AfterFunc. ottimo per i casi in cui vuoi lavorare x secondi dopo che il lavoro è terminato, contro un intervallo coerente. –

risposta

160

La funzione time.NewTicker fa un canale che invia un messaggio periodica, e fornisce un modo per fermarlo. Usalo qualcosa di simile (non testata):

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

si può fermare il lavoratore chiudendo il canale quit: close(quit).

+8

La risposta non è corretta a seconda di ciò che l'OP vuole. Se l'OP vuole un'esecuzione periodica indipendentemente da quanto tempo il lavoratore usa, dovresti eseguire 'do stuff' in una routine di go o altrimenti il ​​prossimo worker verrebbe eseguito immediatamente (quando hai bisogno di più di 5 secondi). – nemo

+2

IMO, dovresti semplicemente "chiudere (esci)" quando vuoi fermare lo scheduler. – Dustin

+0

È necessario il canale di uscita? Non possiamo semplicemente fermare il ticker? –

17

ne dite qualcosa come

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") 
} 

Playground

+3

Un 'time.Ticker' è migliore di' time.After' dove preferiresti mantenere l'attività in programma rispetto a un intervallo arbitrario tra le esecuzioni. – Dustin

+0

Dustin ha ragione. Basta fermare il tempo. Più veloce nel percorso di arresto. – Volker

+4

@Dustin E questo è meglio se si desidera eseguire il lavoro con uno spazio fisso tra la fine e l'inizio delle attività. Né è meglio, sono due casi d'uso diversi. – nos

2

Una risposta più ampia a questa domanda potrebbe considerare l'approccio mattoncino Lego spesso utilizzato in Occam e offerto alla comunità Java tramite JCSP. C'è un ottimo presentation by Peter Welch su questa idea.

Questo approccio plug-and-play si traduce direttamente in Go, perché Go utilizza gli stessi fondamentali del processo di comunicazione sequenziale come Occam.

Quindi, quando si tratta di progettare attività ripetitive, è possibile costruire il proprio sistema come una rete di flussi di dati di componenti semplici (come le goroutine) che scambiano eventi (cioè messaggi o segnali) tramite canali.

Questo approccio è compositivo: ogni gruppo di piccoli componenti può comportarsi come un componente più grande, all'infinito. Questo può essere molto potente perché i complessi sistemi concorrenti sono realizzati con mattoncini di facile comprensione.

Nota: nella presentazione di Welch, che usa la sintassi Occam per i canali, che è ! e ? e questi corrispondono direttamente a ch < - e < -ch in Go.

10

Partenza questa libreria: https://github.com/robfig/cron

esempio come segue:

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() 
14

Se non si cura di zecca spostamento (a seconda di quanto tempo è stato preso in precedenza su ogni esecuzione) e si fa Non voglio usare i canali, è possibile usare la funzione di intervallo nativo.

cioè

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") 
    } 
} 

Playground

Problemi correlati