Master Go Concurrency with sync: Mutex, RWMutex, WaitGroup, Once, Cond & Pool
Learn how Go's sync package provides essential concurrency primitives—Mutex, RWMutex, WaitGroup, Once, Cond, and Pool—through clear examples and best‑practice patterns that prevent data races, deadlocks, and performance pitfalls, enabling safe, elegant, and efficient goroutine coordination.
Why Use sync?
Many Go beginners write code that increments a shared counter from multiple goroutines, which leads to nondeterministic results because of data races. The sync package offers a full set of synchronization primitives to safely access shared data.
for i := 0; i < 10; i++ {
go func() {
counter++
}()
}sync.Mutex
Mutexis the classic mutual‑exclusion lock. Only one goroutine can hold the lock at a time, ensuring exclusive access to critical sections.
var mu sync.Mutex
var counter int
func increment() {
mu.Lock()
counter++
mu.Unlock()
}Tip: Use defer mu.Unlock() right after locking to guarantee the unlock even if a panic occurs.
sync.RWMutex
When the workload is read‑heavy (e.g., caches or configuration tables), RWMutex allows multiple readers simultaneously while still enforcing exclusive access for writers.
var mu sync.RWMutex
var data = make(map[string]string)
func read(key string) string {
mu.RLock()
defer mu.RUnlock()
return data[key]
}
func write(key, value string) {
mu.Lock()
data[key] = value
mu.Unlock()
}Imagine a library where many readers can browse books at once, but when the librarian adds a new book, everyone must leave.
sync.WaitGroup
WaitGroupacts as a counter that makes the main goroutine wait until all spawned goroutines finish.
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
fmt.Println("Worker", id, "done")
}(i)
}
wg.Wait()
fmt.Println("All workers finished")Key methods: Add(n): declare how many tasks to wait for. Done(): signal completion of one task. Wait(): block until the counter reaches zero.
sync.Once
Onceguarantees that a piece of code runs only a single time, which is useful for loading configuration, initializing connection pools, or implementing singletons.
var once sync.Once
func initConfig() {
fmt.Println("Config loaded")
}
func main() {
for i := 0; i < 3; i++ {
go func() {
once.Do(initConfig)
}()
}
}sync.Cond
Condprovides a condition variable for goroutines to wait until a specific condition becomes true, then be signaled or broadcast.
var ready = false
var mu sync.Mutex
var cond = sync.NewCond(&mu)
func worker() {
mu.Lock()
for !ready {
cond.Wait()
}
fmt.Println("Worker proceeding")
mu.Unlock()
}
func main() {
go worker()
time.Sleep(time.Second)
mu.Lock()
ready = true
cond.Signal()
mu.Unlock()
}Core methods: Wait(): block until the condition is signaled. Signal(): wake up one waiting goroutine. Broadcast(): wake up all waiting goroutines.
sync.Pool
Poolcaches temporary objects to reduce allocation overhead and GC pressure, ideal for high‑frequency creation and reuse scenarios such as bytes.Buffer.
var pool = sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
}
func main() {
buf := pool.Get().(*bytes.Buffer)
buf.WriteString("hello")
fmt.Println(buf.String())
buf.Reset()
pool.Put(buf)
}Note: Pool entries are cleared during garbage collection, so it should not be used for storing long‑term important data.
Conclusion
Mastering these synchronization primitives lets you write Go concurrent programs that are safe, orderly, and high‑performance. Think of goroutines as musicians, channels as rhythm, and the sync package as the conductor that keeps the whole orchestra in tune.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Code Wrench
Focuses on code debugging, performance optimization, and real-world engineering, sharing efficient development tips and pitfall guides. We break down technical challenges in a down-to-earth style, helping you craft handy tools so every line of code becomes a problem‑solving weapon. 🔧💻
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
