Why aren't my goroutines launched?

Issue

No, it’s not because my program ends too fast.

I have this script:

package main

import ("log"; "io/ioutil"; "strings")

const BASE_FILE_NAME = "abc_"

func mygoroutine(file_name string) {
    log.Println("In goroutine for file", file_name)
}


func get_file_names() []string {
  file_names := make([]string, 0)
  files, _ := ioutil.ReadDir("./")
  for _, file := range files {
      if strings.HasPrefix(file.Name(), BASE_FILE_NAME) {
        file_names = append(file_names, file.Name())
      }
  }

  return file_names
}

func main()  {
    file_names := get_file_names()
    for _, file_name := range file_names {
        log.Println("Now lunching goroutine for file", file_name)
        go mygoroutine(file_name)
    }

    log.Println("Finished launching.")

    for {}

    log.Println("Now exiting")
}

In the directory that contains the executable, I have two files that start with abc_ so the output is this:

2016/03/04 20:35:14 Now lunching goroutine for file abc_fr
2016/03/04 20:35:14 Now lunching goroutine for file abc_hrty
2016/03/04 20:35:14 Finished launching.

The script doesn’t stop, it never logs Now exiting , because it loops in the empty for. But I do not see the In goroutine for file message.

Why is this happening? What am I doing wrong?

Thanks for the help!

Solution

If your program is running with GOMAXPROCS=1 (that is, a single OS thread), for{} freezes it without ever letting Go’s user-mode scheduler run. This is the issue about it. JimB points out it causes other problems regardless of GOMAXPROCS; eventually the runtime has to stop your goroutine for garbage collection, and it can’t stop for{}.

Changing for{} to select{} lets the scheduler run and doesn’t eat CPU. In this reduced program, your goroutine code runs. It ends with “all goroutines are asleep – deadlock!” because your other goroutine quits and the only remaining one (main) is hung in the select{}.

package main

import "log"

const BASE_FILE_NAME = "abc_"

func mygoroutine(file_name string) {
    log.Println("In goroutine for file", file_name)
}

func main() {
    go mygoroutine("foo")
    log.Println("Finished launching.")
    select {}
    log.Println("Now exiting")
}

Of course, you normally don’t want to hang a goroutine even with select{}; that’s going to leave some resources in use until your program ends. To build something useful you’ll need something else like a sync.WaitGroup or channel.

Answered By – twotwotwo

Answer Checked By – Terry (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.