BudiBadu Logo

Samplebadu

Code with Example
BudiBadu Logo
Samplebadu

Go by Example: WaitGroups

Go 1.23

Coordinate concurrent tasks using WaitGroups. This example demonstrates how to use `sync.WaitGroup` to wait for a collection of goroutines to finish executing, a fundamental pattern for synchronization in Go programs.

Code

package main

import (
    "fmt"
    "sync"
    "time"
)

// worker simulates a task that takes some time to complete
func worker(id int) {
    fmt.Printf("Worker %d starting\n", id)
    
    // Simulate expensive work
    time.Sleep(time.Second)
    
    fmt.Printf("Worker %d done\n", id)
}

func main() {
    // WaitGroup is used to wait for all goroutines launched here to finish
    var wg sync.WaitGroup

    // Launch 5 worker goroutines
    for i := 1; i <= 5; i++ {
        // Increment the WaitGroup counter BEFORE starting the goroutine
        wg.Add(1)

        // Wrap the worker call in a closure to handle the WaitGroup
        go func(id int) {
            // Defer Done() to ensure it runs even if the worker panics
            defer wg.Done()
            worker(id)
        }(i)
    }

    fmt.Println("Main: Waiting for workers to finish...")
    
    // Block until the WaitGroup counter goes back to 0
    wg.Wait()
    
    fmt.Println("Main: All workers completed")
}

Explanation

To wait for multiple goroutines to finish, we use a sync.WaitGroup. This is cleaner and more robust than using time.Sleep or managing multiple channels manually.

A WaitGroup maintains a counter. You increment it with Add() when starting a task, and decrement it with Done() when a task finishes. The Wait() method blocks execution until the counter reaches zero.

Important rules for WaitGroups:

  • Always call Add() *before* starting the goroutine to avoid race conditions
  • Use defer wg.Done() inside the goroutine to ensure it runs
  • Pass WaitGroups by pointer if passing them to functions

Code Breakdown

21
Declaring a sync.WaitGroup. It doesn't need initialization; the zero value is ready to use.
26
Calling wg.Add(1) tells the WaitGroup we are starting 1 new task. This must happen in the main goroutine, not inside the new goroutine.
31
wg.Done() decrements the counter. Using defer ensures it runs when the function exits, preventing deadlocks if the code crashes.
39
wg.Wait() blocks the main function until the counter reaches zero (i.e., all 5 workers have called Done).