Fundamentals 5 min read

Mastering Go Concurrency: When to Use WaitGroup vs Mutex

This article examines Go's sync package, detailing how WaitGroup coordinates the completion of multiple goroutines while Mutex protects shared resources, compares their purposes and use cases, and provides practical code examples to help developers choose the right tool for concurrent programming.

Ops Development & AI Practice
Ops Development & AI Practice
Ops Development & AI Practice
Mastering Go Concurrency: When to Use WaitGroup vs Mutex

Introduction

In Go's concurrency model, correct synchronization of goroutines is essential. The sync package provides primitives such as WaitGroup and Mutex for coordinating execution and protecting shared state.

WaitGroup

Purpose

WaitGroup

waits for a set of goroutines to finish. The counter is increased with Add(delta int) before launching goroutines and decreased with Done() (equivalent to Add(-1)) when a goroutine completes. Wait() blocks until the counter reaches zero.

Typical usage

var wg sync.WaitGroup

for i := 0; i < 5; i++ {
    wg.Add(1)               // increment counter
    go func(id int) {
        defer wg.Done()    // decrement when finished
        // perform work for goroutine id
    }(i)
}

wg.Wait() // blocks until all five goroutines call Done()

Key points

The WaitGroup value must not be copied after first use; pass a pointer if needed.

Calling Add after Wait has started can cause a deadlock.

Typical scenario : coordinating a batch of independent tasks where the program must not proceed until all tasks have completed.

Mutex

Purpose

Mutex

provides mutual exclusion for a critical section, ensuring that only one goroutine accesses the protected data at a time.

Typical usage

var mu sync.Mutex
var counter int

for i := 0; i < 5; i++ {
    go func() {
        mu.Lock()          // acquire exclusive access
        counter++          // modify shared variable safely
        mu.Unlock()        // release lock
    }()
}

Or using defer to guarantee unlock:

go func() {
    mu.Lock()
    defer mu.Unlock()
    counter++
}()

Key points

Never forget to call Unlock; a missed unlock leads to a deadlock.

Lock granularity affects performance; keep critical sections short.

Typical scenario : protecting mutable shared state such as counters, maps, or file handles from race conditions.

Comparison

Goal : WaitGroup synchronizes the completion of goroutines; Mutex protects shared data during concurrent access.

When to use : Use WaitGroup when you need to wait for a set of tasks to finish. Use Mutex when multiple goroutines read/write the same variable.

Conclusion

Properly applying WaitGroup and Mutex enables safe and efficient concurrent programs in Go. They address different aspects of synchronization and are often combined: a WaitGroup can wait for all workers that each use a Mutex to protect shared resources.

References

Go official documentation: sync package

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

concurrencyGoSynchronizationmutexGoroutineWaitGroup
Ops Development & AI Practice
Written by

Ops Development & AI Practice

DevSecOps engineer sharing experiences and insights on AI, Web3, and Claude code development. Aims to help solve technical challenges, improve development efficiency, and grow through community interaction. Feel free to comment and discuss.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.