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
| Method | Description |
|---|---|
| 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.