Goroutine select loop with Ticker causes CPU to 100%

Issue

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

Answered By – icza

Answer Checked By – Dawn Plyler (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.