When main
exits, the program ends and all goroutines shut down. main
must have a way to wait until all goroutines end. You're correct that there are better ways than sleeping; sleeping in concurrent code is a red flag. Here are some basic options.
Simplest is to use a channel.
package mainimport ("fmt""time")func worker(done chan bool) { fmt.Print("working...") time.Sleep(time.Second) fmt.Println("done") done <- true}func main() { done := make(chan bool, 1) go worker(done) <-done}
main makes a channel and passes it into the worker. <-done
causes main to wait until there's something to read. Once worker is done, it sends a true value down the done channel (the value doesn't matter), main reads it and can continue.
For more workers use WaitGroups.
package mainimport ("fmt""sync""time")func worker(id int) { fmt.Printf("Worker %d starting\n", id) time.Sleep(time.Second) fmt.Printf("Worker %d done\n", id)}func main() { var wg sync.WaitGroup for i := 1; i <= 5; i++ { wg.Add(1) go func() { defer wg.Done() worker(i) }() } wg.Wait()}
main creates a sync.WaitGroup. Each time it starts a goroutine it adds 1 to the group. Each worker is wrapped in a goroutine which will call wg.Done
when the work is done subtracting 1 from the group. wg.Wait()
will wait until the WaitGroup is back to 0 indicating all workers are done.