Search code examples
gocpucpu-usagegoroutineticker

Goroutine select loop with Ticker causes CPU to 100%


I have this loop that what it does is trying to repeatedly poll to another server. I used ticker to achieve this however the program repeatedly shows 100% CPU usage.

This ticker runs inside a goroutine. and the HTTP server runs inside another goroutine.


func() Monitor() {

  abort := make(chan bool)

  log.Info("Monitor started.")

  // start the monitor goroutine
  go func() {
      defer log.Info("Stopped monitor")
        
      ticker := time.NewTicker(time.Duration(35.0) * time.Second)
      defer ticker.Stop()
        
      log.Info("Monitor started! \n")
      for {
        select {
        case t := <-ticker.C:
            log.Infof("Subscribe to service at time %v\n", t)
            if err := selfConn.SubscribeToService(); err != nil {
                log.Errorf("Failed to subscribe to primary connector: %v", err)
            } 
        case <-abort:
            log.Info("Finished routine!")
            return
        default:
            continue
        }
        }
    }() 
  
    go func() {
        time.Sleep(10 * time.Minute)
        abort <- true
    }()
}

However when monitor loops start, and everytime the signal to the ticker channel is sent, the CPU shows 100% constantly.

What have I missed in using ticker in the goroutine so that it won't consume 100% CPU?


Solution

  • You have a select with a default branch inside your loop. If none of the other cases are ready to proceed, the default branch is executed immediately, so your next iteration begins immediately without wait. This is a busy loop.

    Also, there is no need for another goroutine to terminate, you could use a timer for that in the same goroutine.

    For example:

    func monitor() {
        log.Info("Monitor started.")
    
        ticker := time.NewTicker(35 * time.Second)
        defer ticker.Stop()
    
        timeoutCh := time.After(10 * time.Minute)
        for {
            select {
            case t := <-ticker.C:
                log.Infof("Subscribe to service at time %v\n", t)
                if err := selfConn.SubscribeToService(); err != nil {
                    log.Errorf("Failed to subscribe to primary connector: %v", err)
                }
            case <-timeoutCh:
                log.Info("Finished routine!")
                return
            }
        }
    }