Reputation: 315
I want to write a worker which does a job say every 1 hour. There has been no crons in our system as of now so don't want to add a cron. Request for suggestion in the followin implementation:
func init(){
go startWorker()
}
func startWorker(){
doJob()
time.Sleep(time.Second * 3600)
}
Is using a sleep a bad idea in a go routine or are there better alternatives to do such stuff.
The job of doJob()
is to fetch from the DB all the failures that have happened in the last hour and do a retry
Upvotes: 1
Views: 3244
Reputation: 5651
There are two issues with using time.Sleep
in the way you envision, one major and one minor.
The goroutine is stuck in an infinite loop, so unless doJob
panics, it will never terminate. You should probably pass it a channel that will be closed when the goroutine needs to terminate:
done := make(chan struct{})
go worker(done)
...
close(done)
...
func worker(done <-chan struct{}){
for {
doJob()
timer := time.NewTimer(time.Hour)
select {
case <-timer.C:
// nothing
case <-done:
timer.Stop()
return
}
}
}
Or, better yet, using a ticker:
func worker(done <-chan struct{}){
ticker := time.NewTicker(time.Hour)
for {
doJob()
select {
case <-ticker.C:
// nothing
case <-done:
ticker.Stop()
return
}
}
}
It is good style here to use defer
:
func worker(done <-chan struct{}){
ticker := time.NewTicker(time.Hour)
defer ticker.Stop()
for {
doJob()
select {
case <-ticker.C:
// nothing
case <-done:
return
}
}
}
While a goroutine uses negligible CPU resources, it uses a stack, on the order of a dozen kilobytes or so. That's probably not a problem for you, but if it is, and if your application has a main loop, then you can insert your ticker and invocation of doWork
into the main select
statement of the main loop.
Upvotes: 2