Learn to identify and fix the most frequent mistakes in Golang.
Using sync.WaitGroup in Go is essential for synchronizing multiple goroutines, but misusing it can lead to deadlocks, crashes, or unpredictable behavior. Below are the most common mistakes and how to avoid them with best practices .
If a goroutine does not call .Done(), the program will hang indefinitely on .Wait(), waiting for a completion signal that never comes.
var wg sync.WaitGroup
wg.Add(1)
go func() {
fmt.Println("Executing task...")
// β Forgot to call wg.Done()
}()
wg.Wait() // β οΈ The program will hang forever because wg.Done() is missing!
fmt.Println("All tasks completed.")
Always call .Done() before exiting the goroutine.
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done() // β
Ensures Done() is executed at the end
fmt.Println("Executing task...")
}()
wg.Wait() // β
Waits correctly for all goroutines to finish
fmt.Println("All tasks completed.")
π‘ Using defer wg.Done() ensures it is always executed, even if an error occurs inside the goroutine.
If .Add(n) is called after goroutines start running, the program may exit before they even execute.
var wg sync.WaitGroup
go func() {
wg.Add(1) // β Incorrect placement, the goroutine might be ignored
defer wg.Done()
fmt.Println("Task running...")
}()
wg.Wait() // β οΈ The program may exit before the goroutine starts!
fmt.Println("All tasks completed.")
Always add goroutines (wg.Add(n)) before launching them.
var wg sync.WaitGroup
wg.Add(1) // β
Add the goroutine before starting it
go func() {
defer wg.Done()
fmt.Println("Task running...")
}()
wg.Wait() // β
Correctly waits for all goroutines to finish
fmt.Println("All tasks completed.")
Once .Wait() is called, adding new goroutines (.Add(n)) can cause unpredictable behavior or even fatal runtime errors.
var wg sync.WaitGroup
wg.Wait() // β Waiting before adding tasks
wg.Add(1) // β οΈ Wrong order, may cause a fatal error
go func() {
defer wg.Done()
fmt.Println("New task running...")
}()
wg.Wait()
fmt.Println("All tasks completed.")
Always add goroutines (.Add(n)) before calling .Wait().
var wg sync.WaitGroup
wg.Add(1) // β
Add tasks BEFORE calling Wait()
go func() {
defer wg.Done()
fmt.Println("New task running...")
}()
wg.Wait() // β
Now, Wait() correctly works
fmt.Println("All tasks completed.")
If .Done() is called more times than .Add(n), the counter becomes negative, causing a fatal runtime error.
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println("Task running...")
wg.Done() // β Extra Done() call, counter goes negative
}()
wg.Wait()
fmt.Println("All tasks completed.")
Make sure each .Add(n) has exactly n .Done() calls.
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done() // β
Only one Done() per goroutine
fmt.Println("Task running...")
}()
wg.Wait() // β
Correct waiting mechanism
fmt.Println("All tasks completed.")
If .Wait() is not called, the main() function might exit before goroutines finish executing.
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println("Task running...")
}() // β οΈ No wg.Wait(), the program may exit too early!
Always use .Wait() to ensure the main() function waits for goroutines to complete.
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println("Task running...")
}()
wg.Wait() // β
Ensures the program waits for goroutines
fmt.Println("All tasks completed.")
Using sync.WaitGroup correctly is crucial for synchronizing multiple goroutines in Go. However, if misused, it can lead to deadlocks, crashes, or unexpected behavior. By following these best practices, you can avoid common pitfalls and make your concurrent Go programs more reliable and efficient. β π₯
π Thank you for reading these articles! If you find this content valuable and want to support my work, a coffee would be greatly appreciated! βπ
π» I am a freelance web developer, and I personally create and maintain this website. Any support would help me improve and expand it further! π