WaitGroup in Go: How to Coordinate Your Goroutines Effectively
Concurrency is one of the hallmarks of the Go programming language. While there are many tools and mechanisms in Go to help you harness the power of concurrency, one of the simplest yet most powerful tools at your disposal is the WaitGroup
from the sync
package.
What is a WaitGroup?
At a high level, a WaitGroup
is a counter. You can increment the counter, decrement the counter, and wait for the counter to reach zero. In the context of concurrent programming, the WaitGroup
allows your program to wait for multiple goroutines to complete their work before continuing. This can be invaluable when you need to ensure that all your goroutines have finished before moving on.
How to Use WaitGroup
Here's a basic outline of how you'd typically use a WaitGroup
:
Declare the
WaitGroup
.Increment the
WaitGroup
counter for every goroutine you launch.Decrement (or "mark done") the
WaitGroup
counter inside each goroutine when it's finished.Use
Wait()
to block until theWaitGroup
counter is zero.
Example:
Let's take a look at a simple example where we spawn multiple goroutines to print numbers and wait for all of them to finish:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1) // Increment the WaitGroup counter
go func(n int) {
defer wg.Done() // Decrement the counter when the goroutine completes
time.Sleep(time.Millisecond * time.Duration(100*n))
fmt.Println(n)
}(i)
}
wg.Wait() // Block until the WaitGroup counter is zero
fmt.Println("All goroutines have finished!")
}
In the above example, we see the wg.Add(1)
method incrementing the counter for each goroutine we spawn. Inside the goroutine, we use defer wg.Done()
to ensure that the counter is decremented once the goroutine completes its work. The wg.Wait()
method blocks the main function until all goroutines are done.
Common Pitfalls:
Forgetting to Add to the Counter: Before launching the goroutine, always remember to increment the counter. If you forget,
Wait()
might not block as you'd expect.Over-Decrementing: If you call
Done()
more times than you've calledAdd()
, theWaitGroup
counter can go negative, which will cause a panic.Relying Solely on WaitGroup for Coordination: While
WaitGroup
is excellent for waiting for goroutines to finish, it doesn't provide more advanced coordination or messaging between goroutines. For those cases, channels or other synchronization primitives might be more appropriate.
Conclusion:
The WaitGroup
in Go's sync
package is a powerful yet straightforward tool for synchronizing the completion of goroutines. By understanding and using it properly, you can ensure your concurrent Go programs are both efficient and correct. Always remember the basics: increment the counter when you launch a goroutine, decrement it when you're done, and use Wait()
to pause until all goroutines have completed.