Skip to content

Latest commit

 

History

History

03_cond

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 

📣 sync.Cond in Go

💡 What is sync.Cond?

A sync.Cond is a synchronization primitive that allows goroutines to wait for or signal events. It provides a way to block a goroutine until some condition is met, and then notify other goroutines when the condition has changed.

This is especialcly useful in scenarios where goroutines need to synchronize and wait for certain conditions, such as when one goroutine is producing data and another is consuming it, or when you want to control access to a shared resource.

📝 Key Concepts

  • Waiting: A goroutine can wait on a sync.Cond object until it is notified that the condition it’s waiting for is satisfied.
  • Signaling: A goroutine can signal others waiting on a condition to proceed when a certain condition is met.
  • Broadcasting: A goroutine can notify all waiting goroutines, allowing them to wake up and re-check the condition.

✅ sync.Cond Methods

  • NewCond(l sync.Locker): This function creates a new condition variable that uses the provided lock (sync.Locker), such as sync.Mutex or sync.RWMutex, to synchronize access.
  • Wait(): The goroutine that calls this method will block until it is notified by another goroutine (via Signal or Broadcast).
  • Signal(): Notifies one goroutine that is waiting on the condition to wake up and re-check the condition.
  • Broadcast(): Notifies all goroutines that are waiting on the condition to wake up and re-check the condition.

✅ Example of Using sync.Cond

package main

import (
	"fmt"
	"sync"
)

var (
	cond  = sync.NewCond(&sync.Mutex{})
	count = 0
)

// Consumer goroutine
func consumer() {
	cond.L.Lock()
	for count <= 0 {
		cond.Wait() // Wait until count > 0
	}
	count--
	fmt.Println("Consumed 1 item, remaining:", count)
	cond.L.Unlock()
}

// Producer goroutine
func producer() {
	cond.L.Lock()
	count++
	fmt.Println("Produced 1 item, total:", count)
	cond.Signal() // Signal a waiting consumer
	cond.L.Unlock()
}

func main() {
	var wg sync.WaitGroup

	// Start 3 consumers
	for i := 0; i < 3; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			consumer()
		}()
	}

	// Start 2 producers
	for i := 0; i < 2; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			producer()
		}() 
	}

	wg.Wait()
}

Key Takeaways:

  • cond.Wait(): Used when a goroutine wants to wait until some condition becomes true. It automatically releases the lock and blocks until it’s notified.
  • cond.Signal(): Used to notify one goroutine that’s waiting on the condition variable. It’s typically used when some state change has occurred and other goroutines may proceed.
  • cond.Broadcast(): Used to notify all waiting goroutines, allowing them to wake up and check their conditions.

✅ When to Use sync.Cond

  • Producer-Consumer Problem: A common scenario where sync.Cond is useful is when one or more goroutines are producing data (e.g., items, messages) and other goroutines are consuming it. Producers need to signal consumers when new data is available, and consumers need to wait when the data is not yet available.

  • Worker Pool: A worker pool where multiple workers wait for tasks and work when the tasks are available.

Why sync.Cond is important compared to other sync primitives:

While tools like sync.Mutex, sync.RWMutex, and sync.WaitGroup are useful for synchronizing goroutines, they cannot handle waiting for a specific condition or signaling other goroutines when a condition is met. Here's where sync.Cond shines:

  1. For synchronizing goroutines based on conditions:

    • sync.Mutex and sync.RWMutex are good for mutual exclusion (ensuring only one goroutine accesses a resource at a time), but they cannot directly handle scenarios where a goroutine needs to wait for a condition to change.
    • sync.WaitGroup helps wait for goroutines to complete, but it doesn't support waiting for specific conditions or notifying others to continue based on dynamic states.
  2. Handling producer-consumer scenarios:

    • If you are implementing producer-consumer patterns, sync.Cond allows consumers to wait for producers to add data to the buffer and ensures that consumers can act when data is available, without constantly polling the buffer. Without sync.Cond, you would need additional mechanisms to handle these waiting/notification states, which could lead to inefficient or error-prone code.
  3. Efficient use of resources:

    • With sync.Cond, a goroutine doesn't constantly check a condition in a tight loop (which could waste CPU resources). It simply waits for a signal and wakes up only when necessary.

🔑 Best Practices

  • Always use a lock with sync.Cond: A sync.Cond must be used with a sync.Locker, such as sync.Mutex or sync.RWMutex, to synchronize access to shared resources.

  • Avoid deadlocks: Ensure that goroutines calling Wait will eventually be notified via Signal or Broadcast. Otherwise, they will remain blocked forever, causing a deadlock.

  • Minimize contention: Only use Signal or Broadcast when necessary, as notifying many goroutines at once can cause high contention and reduce performance.