WaitGroups in Golang
Tags: go, codenewbie, concurrency, waitgroups
Published: 2022-02-22 08:17:59

> This article was originally posted at [ioscript.org](https://ioscript.org/waitgroups-in-golang)

In the previous article, [Introduction to Concurrency in Golang](https://ioscript.org/introduction-to-concurrency-in-golang), of the series,[Concurrency in Golang](https://ioscript.org/series/go-concurrency), we came across a problem where one or more goroutines might not necessarily finish execution before the main goroutine does, thus we were unable to see the message printed by the unfinished goroutine. We fixed the issue by adding `time.Sleep()` in the main goroutine to put it to sleep for a few seconds and thus increasing the probability of other goroutines finishing before the main does. Although our code worked in that particular case, most certainly it will not work for all cases. In this article, we will see, how we can make use of the **WaitGroup **synchronization primitive provided by Go. 

Before we discuss **WaitGroups** let's have a look into Go's concurrency model *fork-join*. 

## Fork-Join Model

In concurrency, Fork-Join Model is a way of setting up and executing concurrent programs, such that execution branches off in designated points and joins or merges at a subsequent point and resumes the sequential execution. The word **Fork** refers to any point of time in the program runtime, it can create one or more *child* branches of execution to be run either concurrently or in some cases parallel to the *parent*. The word **Join** refers to the point in the future when concurrent execution branches
join back to the parent.


![Frame 2.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1645123256809/2nuzK8fhN.png)



## WaitGroups

Now, that we know more about the concurrency model, Fork-Join. Let's go through our code from the last article and try to fix it without putting the main goroutine to sleep.

```
package main

import "fmt"

func sayGreetings() {
    fmt.Println("Hello World!!")
}

func main() {
    go sayGreetings()
}

```

When we run the above code, there will be two possibilities. First, the **sayGreetings** goroutine prints `Hello World!` and complete its execution before the **main** goroutine does. In that case, both Fork and Join operations will happen. In the second case,  the **sayGreetings** goroutine won't be able to complete its execution before the **main ** goroutine does. In such a case, the Join operation will not happen. 

![Frame 2 (1).png](https://cdn.hashnode.com/res/hashnode/image/upload/v1645124232995/ZEZ2rgNG2.png)

To solve the above issue, we need to make sure that the **Join** operation happens in all the cases before the main goroutine completes its execution. We can use synchronization primitive **WaitGroup** provided by the **sync** package of Go's inbuilt library.


A **WaitGroup **waits for a collection of goroutines to finish. We can think of **WaitGroup** as a concurrent-safe counter. WaitGroup provides three methods attached to it.

```
	type WaitGroup

		func (wg *WaitGroup) Add(delta int)
		func (wg *WaitGroup) Done()
		func (wg *WaitGroup) Wait()
		
```

- **`func (wg *WaitGroup) Add(delta int)`** method increments the counter by the integer passed in. Integer passed into the Add method means the number of new goroutines created. 
  
- **`func (wg *WaitGroup) Done()`** method decrements the counter by one. We call the Done method when a goroutine completes its execution.
  
- **`func (wg *WaitGroup) Wait()`** method is used to block the execution until all the goroutines deployed have already completed their execution.




Let's see how to use **WaitGroups** in our previous code

```

package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup    // 1

func sayGreetings() {

	defer wg.Done()      // 3

	fmt.Println("Hello World!!")

}

func main() {

	wg.Add(1)           // 2  
	go sayGreetings()

	wg.Wait()			// 4
}

```

The above example will deterministically block the main goroutine until the sayGreetings goroutine completes its execution 

**Explanation**

1. We created a global variable type WaitGroup from the sync package using `var wg sync.WaitGroup`. 
   
2. In the `main` function we call `wg.Add(1)` just before we started a goroutine `sayGreetings`. This indicates that we have launched one goroutine 
   
3. Here we call `wg.Done()` using defer keyword to make sure that `wg.Done()` is called just before we exit the sayGreetings goroutine closure. This indicates we have completed the execution of one goroutine.
   
4. Here we call `wg.Wait()`, which will block further execution of the `main` goroutine until all the goroutines have completed execution.


Let's see another example on WaitGroups where we launch multiple goroutines.

```
package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup

func sayGreetings(i int) {

	defer wg.Done()

	fmt.Printf("Hello from goroutine %v!!\n", i)

}

func main() {
	
	fmt.Println("Hello from the main goroutine !!")
	
	for i := 0; i < 5; i++ {

		wg.Add(1)
		go sayGreetings(i)

	}

	wg.Wait()
	
	fmt.Println("Bye!")
}
```

**Output**

```
Hello from the main goroutine !!
Hello from goroutine 4!!
Hello from goroutine 0!!
Hello from goroutine 1!!
Hello from goroutine 2!!
Hello from goroutine 3!!
Bye !!
```

We have modified the sayGreetings function to accept an integer value, in order to print the message. Also, we have made certain changes in the `main` function to use a `for` loop to deploy 5 goroutines to print the message. 

It's a good practice to call Add() as closely as possible to the goroutine its tracking. I have called wg.Add(1) just before deploying the goroutine. But you will also find some codes in which wg.Add() is called just before the `for` loop, as shown in the below example. Both ways are correct. we just need to make sure that we use the correct integer value for the Add().

```

	const greetingsCount = 5

	wg.Add(greetingsCount)

	for i:=0; i<greetingsCount; i++ {

		go sayGreetings(i)

	}

	wg.Wait()
```

## Conclusion

In this article, we learned about Go's concurrency model, WaitGroups, and the methods provided with it. We saw how we can use waitgroups to create a join point in our code and make sure all the goroutines deployed get executed completely before the main goroutine exits.

## Before You Leave

If you found this article valuable, you can support us by dropping a like and sharing this article with your friends. 

You can sign up for our newsletter to get notified whenever we post awesome content on Golang.

**Reference**

- [Go docs](https://go.dev/) 

- [Go in Action](https://g.co/kgs/ye6b6C)

- [GoByExample](https://gobyexample.com/)

- [Concurrency in Go](https://g.co/kgs/8DKFH5)

Check our ioscript.org for more such contents.

![Frame 1.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1645039088124/j9q9kQHgH.png)