Table of Contents
- Overview
- Prerequisites
- Setting Up Go
- Understanding WaitGroups
- Using WaitGroups in Go
- Example: Parallel Image Processing
- Conclusion
Overview
In Go, concurrency is achieved using goroutines, which are lightweight threads of execution. However, when working with goroutines, we often need to wait for all of them to complete before proceeding further. This is where WaitGroups come in handy. WaitGroups provide a mechanism to wait for a collection of goroutines to finish their execution.
In this tutorial, we will explore how to work with WaitGroups in Go. By the end of this tutorial, you will have a clear understanding of how to use WaitGroups to coordinate the execution of multiple goroutines and handle their synchronization.
Prerequisites
To follow along with this tutorial, you should have a basic understanding of the Go programming language and its syntax. If you are new to Go, you can refer to the Go Tour or the official Go documentation to familiarize yourself with the language.
Setting Up Go
Before we dive into using WaitGroups, make sure you have Go installed on your machine. You can download Go from the official website (https://golang.org/dl/) and follow the installation instructions for your operating system.
To verify that Go is correctly installed, open a terminal or command prompt and run the following command:
go version
You should see the version number of Go printed on the screen, indicating that the installation was successful.
Understanding WaitGroups
A WaitGroup is a synchronization primitive provided by the sync
package in Go. It is used to wait for a collection of goroutines to finish their execution. The WaitGroup maintains a counter internally, which is incremented every time a new goroutine is spawned and decremented when a goroutine completes its execution.
A WaitGroup has three main methods:
Add(delta int)
: Adds the specified delta to the WaitGroup’s counter. If delta is negative, it subtracts the absolute value of delta from the counter.Done()
: Decrements the WaitGroup’s counter by one. Equivalent to callingAdd(-1)
.Wait()
: Blocks the current goroutine until the WaitGroup’s counter becomes zero.
By using these methods, we can control the synchronization of goroutines in our Go programs.
Using WaitGroups in Go
Let’s now see how to use WaitGroups in a Go program. We will go through a step-by-step example that demonstrates the usage of WaitGroups to perform parallel image processing.
-
Start by creating a new Go file named
main.go
and open it in your favorite text editor. -
Import the necessary packages at the top of the file:
package main import ( "fmt" "sync" )
-
Define the main function that will be executed when the program is run:
func main() { // Create a WaitGroup var wg sync.WaitGroup // Add the number of goroutines to the WaitGroup wg.Add(3) // Start the goroutines go processImage("image1.jpg", &wg) go processImage("image2.jpg", &wg) go processImage("image3.jpg", &wg) // Wait for all the goroutines to finish wg.Wait() // All goroutines have completed fmt.Println("All images processed.") }
In the code above, we create a WaitGroup
wg
and add the number of goroutines we want to wait for using theAdd
method. In this case, we want to wait for 3 goroutines to finish. We then start three goroutines using thego
keyword, each calling theprocessImage
function with a different image file name and passing the WaitGroupwg
as an argument. -
Implement the
processImage
function that simulates image processing:func processImage(filename string, wg *sync.WaitGroup) { // Print a message indicating the start of image processing fmt.Printf("Processing %s...\n", filename) // Simulate some image processing // ... // Decrement the WaitGroup counter when the goroutine is done wg.Done() }
The
processImage
function takes the file name of the image to be processed and the WaitGroupwg
as parameters. Inside the function, we simulate some image processing and then call theDone
method on the WaitGroup to decrement its counter, indicating that the goroutine has completed. -
Save the file and build/run the program using the following command:
go run main.go
You should see the messages indicating the start of image processing for each image, followed by the message “All images processed.” when all goroutines have completed.
Congratulations! You have successfully used WaitGroups to synchronize the execution of multiple goroutines in Go.
Example: Parallel Image Processing
Now that you understand how to use WaitGroups, let’s take it a step further and perform parallel image processing using goroutines and WaitGroups.
-
In the
processImage
function, replace the simulated image processing with actual image processing code that loads an image file, applies a filter, and saves the processed image. -
Modify the main function to process multiple images in parallel:
func main() { var wg sync.WaitGroup images := []string{"image1.jpg", "image2.jpg", "image3.jpg"} for _, img := range images { wg.Add(1) go func(filename string) { defer wg.Done() processImage(filename) }(img) } wg.Wait() fmt.Println("All images processed.") }
In the modified code above, we create a slice of image file names and iterate over them using a
for
loop. For each image, we add 1 to the WaitGroup counter and spawn a goroutine that calls an anonymous function. The anonymous function takes the image file name as an argument, defers the call towg.Done()
, and then callsprocessImage
with the file name.This way, all the images will be processed in parallel, and the main goroutine will wait for all the goroutines to finish before printing the final message.
-
Build and run the updated program:
go run main.go
You should see the image processing messages appearing in a non-deterministic order, indicating that the processing is happening in parallel. Once all the images are processed, the final message “All images processed.” will be printed.
Conclusion
In this tutorial, we learned how to work with WaitGroups in Go to coordinate the execution of multiple goroutines. We explored the usage of WaitGroups through a step-by-step example of parallel image processing. By utilizing WaitGroups effectively, we can achieve efficient and synchronized concurrency in our Go programs.
WaitGroups are just one of the many valuable concurrency primitives provided by the Go language. So, keep exploring and experimenting with Go’s concurrent programming features to unlock its full potential.
Happy coding!