Synchronizing Goroutine Completion with WaitGroups
In Go, when you launch multiple goroutines to perform concurrent tasks, you often need a way to ensure that the main program doesn't exit before all goroutines have finished their work. This is where
sync.WaitGroup
What is a WaitGroup?
sync.WaitGroup
Wait()
WaitGroup is a counter for goroutine completion.
WaitGroup helps the main program wait for all launched goroutines to finish their tasks before proceeding or exiting.
The sync.WaitGroup
type has three primary methods:
Add(delta int)
: Increments theWaitGroup
counter bydelta
. Typically, you callAdd(1)
before launching each goroutine.Done()
: Decrements theWaitGroup
counter by one. This should be called by each goroutine when it finishes its work, often usingdefer
to ensure it's called even if the goroutine panics.Wait()
: Blocks until theWaitGroup
counter is zero. The main goroutine or any other goroutine that needs to wait for the completion of the group calls this method.
How to Use WaitGroups
Using
WaitGroup
WaitGroup
Done()
Wait()
Loading diagram...
sync.WaitGroup
and what do they do?Add(delta int) increments the counter, Done() decrements the counter, and Wait() blocks until the counter is zero.
It's crucial to call
wg.Add()
wg.Wait()
wg.Add()
Always call wg.Add()
before launching the goroutine to avoid race conditions.
Similarly,
defer wg.Done()
Done()
Example Scenario
Imagine a web scraper that needs to fetch data from multiple URLs concurrently. You can use
WaitGroup
Consider a scenario where you want to process a list of numbers concurrently. Each goroutine will square a number and print it. A WaitGroup
ensures the main function waits for all squaring operations to finish.
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // Signal that this goroutine is done
fmt.Printf("Worker %d starting\n", id)
// Simulate some work
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) // Increment counter for each goroutine
go worker(i, &wg) // Launch goroutine
}
wg.Wait() // Block until all goroutines are done
fmt.Println("All workers have finished.")
}
In this code, wg.Add(1)
is called before go worker(i, &wg)
. Inside worker
, defer wg.Done()
ensures the counter is decremented. Finally, wg.Wait()
in main
pauses execution until all 5 workers have called Done()
.
Text-based content
Library pages focus on text content
Key Takeaways
Using
sync.WaitGroup
- Initialize a .codeWaitGroup
- Call before launching each goroutine.codeAdd(1)
- Use inside each goroutine.codedefer wg.Done()
- Call in the goroutine that needs to synchronize.codewg.Wait()
Learning Resources
This official Go blog post discusses concurrency patterns, including a practical example of using WaitGroups in pipeline processing.
A clear and concise tutorial explaining the purpose and usage of sync.WaitGroup with code examples.
Provides a step-by-step guide on how to use WaitGroup, covering Add, Done, and Wait methods with illustrative code.
A Medium article that delves into the mechanics of WaitGroup and common pitfalls to avoid.
A video tutorial demonstrating the use of WaitGroups in Go for synchronizing goroutines.
The official Go documentation for the `sync` package, detailing the `WaitGroup` type and its methods.
Part of the official Go documentation, this section covers concurrency primitives, including WaitGroups, in the context of writing effective Go code.
GeeksforGeeks provides an explanation of goroutines and how WaitGroups are used to manage their execution flow.
An in-depth article from Ardan Labs discussing the nuances and best practices for using WaitGroups in Go applications.
A straightforward example demonstrating a common use case for WaitGroup in Go.