Sling Academy
Home/Golang/Synchronization with `sync.WaitGroup` in Go

Synchronization with `sync.WaitGroup` in Go

Last updated: November 27, 2024

In concurrent programming, handling synchronization is crucial to ensure smooth operation and correct results. In Go, sync.WaitGroup is a powerful synchronization tool to manage the execution of goroutines and wait for their completion before proceeding.

Understanding sync.WaitGroup

The sync.WaitGroup is part of Go's sync package. It provides a simple mechanism to wait for a collection of goroutines to finish without requiring complex logic or imposing performance overhead.

Key Methods of sync.WaitGroup

  • Add(int): Increment the wait group counter by the specified value.
  • Done(): Decrement the wait group counter by one. Typically called as a goroutine completes its task.
  • Wait(): Block execution until the counter becomes zero.

Using sync.WaitGroup: A Basic Example

Let's look at a basic example where we use sync.WaitGroup to manage three concurrent goroutines:

package main

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

func worker(id int, wg *sync.WaitGroup) {
  defer wg.Done() // Notify the WaitGroup that this worker is done
  fmt.Printf("Worker %d starting\n", id)
  time.Sleep(time.Second) // Simulate work with sleep
  fmt.Printf("Worker %d done\n", id)
}

func main() {
  var wg sync.WaitGroup

  for i := 1; i <= 3; i++ { // Start 3 workers
    wg.Add(1) // Increment the counter for each worker
    go worker(i, &wg)
  }

  wg.Wait() // Wait for all workers to complete
  fmt.Println("All workers completed.")
}

In the example above, we created three goroutines identified as worker 1, worker 2, and worker 3. The WaitGroup was used to track their completion and ensure that "All workers completed." message is printed only after all goroutines have finished.

Advanced Example: Error Handling and Synchronization

Sometimes, after launching goroutines, you may also want to handle errors returned by these goroutines. Here's a pattern you can use:

package main

import (
  "fmt"
  "sync"
  "errors"
)

func workerWithErr(id int, wg *sync.WaitGroup, errs chan<- error) {
  defer wg.Done()
  fmt.Printf("Worker %d starting\n", id)

  // Simulate some work with a potential error outcome
  if id == 2 { // Example condition where a worker might fail
    errs <- errors.New(fmt.Sprintf("Worker %d encountered an error", id))
  }

  fmt.Printf("Worker %d done\n", id)
}

func main() {
  var wg sync.WaitGroup
  errs := make(chan error, 3) // Capacity set to the number of goroutines

  for i := 1; i <= 3; i++ {
    wg.Add(1)
    go workerWithErr(i, &wg, errs)
  }

  wg.Wait() // Ensure all workers finish
  close(errs) // Close the error channel

  for err := range errs {
    if err != nil {
      fmt.Println(err)
    }
  }

  fmt.Println("All workers completed with error handling.")
}

In this example, the worker function might potentially return an error, which is pushed onto an error channel. The main function handles these potential errors after confirming all goroutines have finished.
This method does not halt execution mid-stream due to an error but awaits all tasks' completion, potentially valuable in scenarios immune to partial failures.

Conclusion

The sync.WaitGroup is among Go's most efficient tools for task synchronization. By utilizing sync.WaitGroup, you can systematically execute parallel computing processes and effectively incorporate error management patterns where applicable.

Next Article: Avoiding Race Conditions in Go Programs

Previous Article: Worker Pools in Go: Managing Concurrent Tasks

Series: Concurrency and Synchronization in Go

Golang

Related Articles

You May Also Like

  • How to remove HTML tags in a string in Go
  • How to remove special characters in a string in Go
  • How to remove consecutive whitespace in a string in Go
  • How to count words and characters in a string in Go
  • Relative imports in Go: Tutorial & Examples
  • How to run Python code with Go
  • How to generate slug from title in Go
  • How to create an XML sitemap in Go
  • How to redirect in Go (301, 302, etc)
  • Using Go with MongoDB: CRUD example
  • Auto deploy Go apps with CI/ CD and GitHub Actions
  • Fixing Go error: method redeclared with different receiver type
  • Fixing Go error: copy argument must have slice type
  • Fixing Go error: attempted to use nil slice
  • Fixing Go error: assignment to constant variable
  • Fixing Go error: cannot compare X (type Y) with Z (type W)
  • Fixing Go error: method has pointer receiver, not called with pointer
  • Fixing Go error: assignment mismatch: X variables but Y values
  • Fixing Go error: array index must be non-negative integer constant