How to wait for all goroutines to finish in Golang

Go has a dedicated package sync that simplifies syncrhonization of goroutines. However in simple cases, goroutines can be synchronized with ordinary channels. In this post we will look at both methods on example.

Waiting for a single goroutine

Waiting for a single goroutine can be implemented with a help of a channel. When finished execution, worker goroutine sends a message to main goroutine awaiting for it. Like in the following example.

package main

import (  
        "fmt"
        "time"
)

func worker(finished chan bool) {  
    fmt.Println("Worker: Started")
    time.Sleep(time.Second)
    fmt.Println("Worker: Finished")
    finished <- true
}

func main() {  
    finished := make(chan bool)

    fmt.Println("Main: Starting worker")
    go worker(finished)

    fmt.Println("Main: Waiting for worker to finish")
    <- finished
    fmt.Println("Main: Completed")
}

Running the code produces the following output:

Main: Starting worker  
Main: Waiting for worker to finish  
Worker: Started  
Worker: Finished  
Main: Completed  

Waiting for multiple goroutines

If we need to wait for multiple goroutines to complete, sync.WaitGroup goes to the rescue. A WaitGroup allows to wait for a collection of goroutines to finish. The main goroutine calls Add to set the number of goroutines to wait for. Then each of the worker goroutines runs and calls Done when finished. At the same time, Wait can be used to block until all goroutines have finished. The following example illustrates it.

package main

import (  
        "fmt"
        "sync"
        "time"
)

func worker(wg *sync.WaitGroup, id int) {  
    defer wg.Done()

    fmt.Printf("Worker %v: Started\n", id)
    time.Sleep(time.Second)
    fmt.Printf("Worker %v: Finished\n", id)
}

func main() {  
    var wg sync.WaitGroup

    for i := 0; i < 5; i++ {
        fmt.Println("Main: Starting worker", i)
        wg.Add(1)
        go worker(&wg, i)
    }

    fmt.Println("Main: Waiting for workers to finish")
    wg.Wait()
    fmt.Println("Main: Completed")
}

Running the code produces the following output:

Main: Starting worker 0  
Main: Starting worker 1  
Main: Starting worker 2  
Main: Starting worker 3  
Main: Starting worker 4  
Main: Waiting for workers to finish  
Worker 4: Started  
Worker 1: Started  
Worker 2: Started  
Worker 0: Started  
Worker 3: Started  
Worker 3: Finished  
Worker 1: Finished  
Worker 4: Finished  
Worker 2: Finished  
Worker 0: Finished  
Main: Completed  

Want to learn more?

Watch GopherCon 2014 A Channel Compendium by John Graham-Cumming. It has very interesting insights on how to use channels for synchronization in Go.

comments powered by Disqus