WaitGroup in Go

WaitGroup in Go

In this tutorial, we are going to discuss WaitGroup in the Go language. Let’s dive straight in and look at what a WaitGroup is and what problem it solves for us.

WaitGroup in Go

Before discussing WaitGroup, we will see a common problem with the Goroutines and try to solve it. Let’s see a simple code snippet illustrating this problem,

package main 

import "fmt"

func runner1() { 
    fmt.Println("I am first runner") 
} 

func runner2() { 
    fmt.Println("I am second runner") 
} 

func execute() { 
    go runner1() 
    go runner2() 
} 

func main() { 
    // Launching both the runners 
    execute() 
}

Run in playground

As you just saw, there was nothing in the output because as soon as you launch both the Goroutines, your main function just got terminated.

So every program in Golang executes until the main function is not terminated. So, what can we do with this problem?

Solution 1

We can wait for some time after launching the runners. For this purpose, we will use the “time” packages function “Sleep”, which pauses the execution of function for a given duration.

package main 

import "fmt"
import "time"

func runner1() { 
    fmt.Println("I am first runner") 
} 

func runner2() { 
    fmt.Println("I am second runner") 
} 

func execute() { 
    go runner1() 
    go runner2() 
} 

func main() { 
    // Launching both the runners 
    execute() 
    time.Sleep(time.Second) 
}

Output

I am first runner 
I am second runner

Run in playground

We just solved the problem. After launching our runners, we waited for a second, so our main function was sleeping(blocked) for 1 sec. In that duration, all of the goroutines were executed successfully.

But Go language is a fast language, and It doesn’t take 1 sec to print 2 strings.

The problem is that our executors execute in a bit amount of time, so we are unnecessarily blocking the program for 1 sec.

In this example, it doesn’t seem to be a critical problem, but if you make a production-grade server that will serve 1000’s requests concurrently, this will be a big problem.

Solution 2

Let’s use another Golang’s standard library primitive “sync.WaitGroup“. WaitGroup is a type of counter which blocks the execution of the function (or might say A goroutine) until its internal counter become 0.

A WaitGroup is used to wait for a collection of Goroutines to finish executing. The control is blocked until all Goroutines finish executing.

Let’s say we have 3 concurrently executing Goroutines issued from the main Goroutine. The main Goroutines needs to wait for the 3 other Goroutines to finish before terminating. This can be accomplished using WaitGroup.

How It Works ?

WaitGroup is a struct that is part of sync package in Go. It is generally used to wait for a collection of goroutines to finish execution. The WaitGroup has the below methods.

  • The Add method is used to add a counter to the WaitGroup.
  • The Done method which is called by each of the goroutines when finished.
  • The Wait method which is used to block till all goroutines have finished and have called the Done method.

Note

WaitGroup is concurrency safe, so its safe to pass pointer to it as argument for Groutines.

Lets stop the theory and write some code right away 😀

package main

import (
    "fmt"
    "sync"
)

func runner1(wg *sync.WaitGroup) {
    defer wg.Done() // This decreases counter by 1
    fmt.Println("I am first runner")
}

func runner2(wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Println("I am second runner")
}

func execute() {
    wg := new(sync.WaitGroup)
    wg.Add(2)
    // We are increasing the counter by 2 because we have 2 goroutines 
    go runner1(wg) 
    go runner2(wg) 
  
   // This Blocks the execution until its counter become 0    
   wg.Wait()
}

func main() {
    // Launching both the runners
    execute()
}

Output

I am first runner 
I am second runner

Run in playground

WaitGroup in Go

Scroll to top