Sling Academy
Home/Golang/Custom Synchronization Primitives with `sync/Cond`

Custom Synchronization Primitives with `sync/Cond`

Last updated: November 27, 2024

In concurrent programming within Go, the sync package does a lot of heavy lifting. Among its offerings, the sync.Cond type provides the building blocks necessary for crafting custom synchronization primitives safely. Let’s delve into using sync.Cond for achieving concurrent execution with signal-based synchronization.

Understanding sync.Cond

sync.Cond is a struct designed to manage goroutines with a condition. It ties together a mutex and a waiting condition, allowing goroutines to signal each other when state changes occur.

The basic operations for a sync.Cond are:

  • Wait(): A call to this method suspends execution of the calling goroutine’s case, releasing the associated lock.
  • Signal(): This method wakes up one of the goroutines waiting on the Cond (if there’s any).
  • Broadcast(): Invoked to wake all waiting goroutines.

Basic Example of sync.Cond

Below is a simple example illustrating how sync.Cond can be set up and used:


package main

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

var mu sync.Mutex
var cond = sync.NewCond(&mu)
var ready = false

func waitGoroutine() {
    cond.L.Lock()
    for !ready {
        cond.Wait()
    }
    fmt.Println("Goroutine: Received signal")
    cond.L.Unlock()
}

func signalFunction() {
    time.Sleep(2 * time.Second) // Simulate work
    cond.L.Lock()
    ready = true
    fmt.Println("Signaler: Sending signal")
    cond.Signal()
    cond.L.Unlock()
}

func main() {
    go waitGoroutine()
    signalFunction()
}

In this example, the waitGoroutine waits until the ready boolean becomes true. On reaching readiness, the signalFunction signals the change, allowing the waiting goroutine to resume.

Crafting Custom Synchronization

Beyond simple signaling, sync.Cond can help in creating advanced primitives. Consider a simple semaphore pattern:


package main

import (
    "fmt"
    "sync"
)

type Semaphore struct {
    v    int
    mtx  sync.Mutex
    cond *sync.Cond
}

func (s *Semaphore) Acquire() {
    s.mtx.Lock()
    for s.v <= 0 {
        s.cond.Wait()
    }
    s.v--
    s.mtx.Unlock()
}

func (s *Semaphore) Release() {
    s.mtx.Lock()
    s.v++
    s.cond.Signal()
    s.mtx.Unlock()
}

func NewSemaphore(initial int) *Semaphore {
    sem := Semaphore{v: initial}
    sem.cond = sync.NewCond(&sem.mtx)
    return &sem
}

func main() {
    sem := NewSemaphore(1)

    var wg sync.WaitGroup
    wg.Add(2)

    go func() {
        defer wg.Done()
        sem.Acquire()
        fmt.Println("Goroutine 1: acquired the semaphore")
        sem.Release()
    }()

    go func() {
        defer wg.Done()
        sem.Acquire()
        fmt.Println("Goroutine 2: acquired the semaphore")
        sem.Release()
    }()

    wg.Wait()
}

Here, a semaphore is implemented with an adjustable initial value, allowing controlled access to a limited resource, demonstrating a real-world application of sync.Cond.

Conclusion

The sync.Cond type offers powerful capabilities for signaling and coordinating state among Go’s goroutines. By harnessing these features, you can create efficient, custom synchronization methods tailored for specific application needs.

Next Article: Monitoring Goroutine Execution with Tracing Tools in Go

Previous Article: Reordering Execution with Goroutines in Go

Series: Concurrency and Synchronization in Go

Golang

Related Articles

You May Also Like

  • How to remove HTML tags in a string in Go
  • How to remove special characters in a string in Go
  • How to remove consecutive whitespace in a string in Go
  • How to count words and characters in a string in Go
  • Relative imports in Go: Tutorial & Examples
  • How to run Python code with Go
  • How to generate slug from title in Go
  • How to create an XML sitemap in Go
  • How to redirect in Go (301, 302, etc)
  • Using Go with MongoDB: CRUD example
  • Auto deploy Go apps with CI/ CD and GitHub Actions
  • Fixing Go error: method redeclared with different receiver type
  • Fixing Go error: copy argument must have slice type
  • Fixing Go error: attempted to use nil slice
  • Fixing Go error: assignment to constant variable
  • Fixing Go error: cannot compare X (type Y) with Z (type W)
  • Fixing Go error: method has pointer receiver, not called with pointer
  • Fixing Go error: assignment mismatch: X variables but Y values
  • Fixing Go error: array index must be non-negative integer constant