
sync.WaitGroup vs errgroup in Go: A Complete Comparison Guide
Admin • April 1, 2025
Let’s compare sync.WaitGroup
with golang.org/x/sync/errgroup
(commonly
called errgroup
) in simple terms. They’re both tools in Go for managing
goroutines, but they have different purposes and features.
sync.WaitGroup
- What it does: Waits for a group of goroutines to finish.
- Focus: Simple synchronization—making sure all tasks complete.
- Error handling: Doesn’t handle errors itself. You’d need to manage errors manually (e.g., with channels).
- Use case: When you just need to wait for tasks to finish, and you don’t care about errors or results in a fancy way.
from earlier:
Example Recapvar wg sync.WaitGroup
wg.Add(2)
go func() { fmt.Println("Hello"); wg.Done() }()
go func() { fmt.Println("World"); wg.Done() }()
wg.Wait()
- Waits for 2 goroutines to finish. No error handling.
errgroup.Group
- What it does: Waits for goroutines to finish and collects errors from them.
- Focus: Synchronization + error management.
- Error handling: Automatically gathers the first error from any goroutine and lets you access it.
- Use case: When you’re running tasks that might fail, and you want to know if something went wrong.
Key Features:
eg.Go()
: Starts a goroutine under theerrgroup
.eg.Wait()
: Waits for all goroutines to finish and returns the first error (if any).- No need for
Add
orDone
—it’s simpler to use thanWaitGroup
in some ways.
Simple Example:
package main
import (
"errors"
"fmt"
"time"
"golang.org/x/sync/errgroup"
)
func main() {
var eg errgroup.Group
// Task 1: Prints "Hello", no error
eg.Go(func() error {
time.Sleep(1 * time.Second)
fmt.Println("Hello")
return nil // No error
})
// Task 2: Prints "World", returns an error
eg.Go(func() error {
time.Sleep(2 * time.Second)
fmt.Println("World")
return errors.New("oops, something went wrong")
})
// Wait for all tasks and check for errors
if err := eg.Wait(); err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("All tasks are complete!")
}
}
Output:
Hello // After 1 second
World // After 2 seconds
Error: oops, something went wrong
Key Differences
Feature | sync.WaitGroup |
errgroup.Group |
---|---|---|
Purpose | Wait for tasks to finish | Wait for tasks + handle errors |
Error Handling | None (DIY with channels, etc.) | Built-in, returns first error |
Setup | Add , Done , Wait |
Just Go and Wait |
Complexity | Simpler for basic use | Slightly more advanced |
Goroutine Limit | Manual tracking with Add |
No manual counting |
When to Use Which?
sync.WaitGroup
:- You just need to wait for goroutines to finish.
- Errors aren’t a big deal, or you’ll handle them separately.
- Example: Running independent tasks like logging or printing.
errgroup.Group
:- You’re running tasks that might fail (e.g., fetching data, API calls).
- You want to stop and know if any task fails.
- Example: Fetching multiple URLs where one might timeout.
errgroup
with Context
Bonus: errgroup
also supports context.Context
for cancellation. If one goroutine
fails, you can cancel the others. WaitGroup
doesn’t have this built-in.
Quick Comparison Example
WaitGroup
(no errors):
With var wg sync.WaitGroup
wg.Add(1)
go func() { fmt.Println("Task"); wg.Done() }()
wg.Wait()
fmt.Println("Done")
errgroup
(with error):
With var eg errgroup.Group
eg.Go(func() error { fmt.Println("Task"); return errors.New("fail") })
if err := eg.Wait(); err != nil {
fmt.Println("Error:", err)
}
Related post: