2

Is it possible to use Ticker to implemented the graceful termination of a long running daemon process? I read the other related thread on here that you should always close the channel to avoid memory leak, but if I run this in a daemon mode (let said I use daemonize to handle the daemon operation outside of golang), and there's really no way for it to do any collective cleanup before the process is terminated. Unless I'm missing something, I'm here to ask whether there's an alternative/better to do this in Golang

func main() {
  ticker := time.NewTicker(Interval)
  workers := make(chan bool, 1)

  for t := range ticker.C {
    select {
      case <- ticker.C:
        log.Println("Scheduled task is triggered.", t)
        go runWorker(workers)
      case <- workers:
        log.Println("Scheduled task is completed.")
        // can't return, it needs to be continue running
    }
  }
}
Community
  • 1
  • 1
samxiao
  • 2,587
  • 5
  • 38
  • 59

2 Answers2

3

I'm not sure I fully understand your goal, however you could always use signal.Notify:

func main() {
    ticker := time.NewTicker(Interval)
    workers := make(chan bool, 1)
    death := make(chan os.Signal, 1)
    signal.Notify(death, os.Interrupt, os.Kill)

    for {
        select {
        case <-ticker.C:
            log.Println("Scheduled task is triggered.", t)
            go runWorker(workers)
        case <-workers:
            log.Println("Scheduled task is completed.")
            // can't return, it needs to be continue running
        case <- death:
            //do any clean up you need and return
        }
    }
}
OneOfOne
  • 95,033
  • 20
  • 184
  • 185
1

Your main function reads the ticker channel twice, once after a tick and then again after another tick before proceeding, which is probably not what you want (if Interval was 30 minutes then you would only run a goroutine every 60 minutes). This would be a better approach:

func main() {
  ticker := time.NewTicker(Interval)
  workers := make(chan bool, 1)

  for {
    select {
      case <- ticker.C:
        log.Println("Scheduled task is triggered.", t)
        go runWorker(workers)
      case <- workers:
        log.Println("Scheduled task is completed.")
        // can't return, it needs to be continue running
    }
  }
}

This will continue creating a goroutine after each interval. You don't need to close this channel until your loop is finished. If your application just exits then your channel will be cleaned up safely. If you need to stop the loop early then just make sure to call ticker.Stop() before you exit the loop.

If you just want to run a goroutine once after an interval, use time.AfterFunc

Ian Davis
  • 1,209
  • 1
  • 9
  • 8