0
0
GoHow-ToBeginner · 4 min read

How to Use sync.Cond in Go: Simple Guide with Examples

In Go, sync.Cond is used to coordinate goroutines by allowing them to wait for or signal events. You create a sync.Cond with a locker (usually a sync.Mutex), then use Wait() to pause a goroutine and Signal() or Broadcast() to wake waiting goroutines.
📐

Syntax

The sync.Cond type is created with a locker, typically a sync.Mutex. It provides three main methods:

  • Wait(): Releases the lock and waits to be signaled.
  • Signal(): Wakes one waiting goroutine.
  • Broadcast(): Wakes all waiting goroutines.

Use Lock() and Unlock() on the locker to protect shared state.

go
var mu sync.Mutex
cond := sync.NewCond(&mu)

// In goroutine:
mu.Lock()
for !condition {
    cond.Wait() // Wait releases mu and blocks
}
// condition met, proceed
mu.Unlock()

// To wake one waiting goroutine:
cond.Signal()

// To wake all waiting goroutines:
cond.Broadcast()
💻

Example

This example shows two goroutines: one waits for a condition to become true, and the other signals when it changes. It demonstrates how sync.Cond coordinates goroutines safely.

go
package main

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

func main() {
	var mu sync.Mutex
	cond := sync.NewCond(&mu)
	ready := false

	// Waiting goroutine
	go func() {
		mu.Lock()
		for !ready {
			cond.Wait() // Wait until ready is true
		}
		fmt.Println("Goroutine: Condition met, proceeding")
		mu.Unlock()
	}()

	// Simulate work then signal
	time.Sleep(1 * time.Second)
	mu.Lock()
	ready = true
	cond.Signal() // Wake one waiting goroutine
	mu.Unlock()

	// Wait to see output
	time.Sleep(500 * time.Millisecond)
}
Output
Goroutine: Condition met, proceeding
⚠️

Common Pitfalls

Common mistakes when using sync.Cond include:

  • Calling Wait() without holding the lock, which causes a runtime panic.
  • Not using a loop to check the condition before and after Wait(), which can lead to spurious wakeups causing incorrect behavior.
  • Forgetting to lock and unlock the mutex around condition checks and signaling.

Always use a loop to check the condition when waiting, and hold the lock when calling Wait(), Signal(), or Broadcast().

go
/* Wrong way: missing lock before Wait() */
// cond.Wait() // causes panic: sync: unlock of unlocked mutex

/* Correct way: */
mu.Lock()
for !condition {
    cond.Wait()
}
mu.Unlock()
📊

Quick Reference

MethodDescription
Wait()Releases lock and waits to be signaled; reacquires lock before returning
Signal()Wakes one goroutine waiting on the condition
Broadcast()Wakes all goroutines waiting on the condition
NewCond(l Locker)Creates a new condition variable with the given locker (usually *sync.Mutex)

Key Takeaways

Always hold the lock when calling Wait(), Signal(), or Broadcast() on sync.Cond.
Use a loop to check the condition before and after Wait() to handle spurious wakeups.
Signal() wakes one waiting goroutine; Broadcast() wakes all waiting goroutines.
sync.Cond helps coordinate goroutines waiting for shared state changes safely.
Forget not to unlock the mutex after signaling to avoid deadlocks.