Go Cond: Difference between revisions

From NovaOrdis Knowledge Base
Jump to navigation Jump to search
Line 32: Line 32:


Each <code>Cond</code> has an associated <code>Locker</code> <code>L</code>, commonly a <code>*Mutex</code> or <code>*RWMutex</code>, which must be held when changing the condition and when calling the <code>Wait</code> method. A <code>Cond</code> must not be copied after first use. In the terminology of the Go memory model, <code>Cond</code> arranges that a call to <code>Broadcast()</code> or <code>Signal()</code> "synchronizes before" any <code>Wait</code> call that it unblocks.
Each <code>Cond</code> has an associated <code>Locker</code> <code>L</code>, commonly a <code>*Mutex</code> or <code>*RWMutex</code>, which must be held when changing the condition and when calling the <code>Wait</code> method. A <code>Cond</code> must not be copied after first use. In the terminology of the Go memory model, <code>Cond</code> arranges that a call to <code>Broadcast()</code> or <code>Signal()</code> "synchronizes before" any <code>Wait</code> call that it unblocks.
=Example=
<syntaxhighlight lang='go'>
var cond = sync.NewCond(&sync.Mutex{})
var remaining = []string{"red", "green", "blue", "yellow", "black", "fuchsia"}
var next string
func painter(myColor string) {
cond.L.Lock()
// wait for my turn, "sleep" otherwise
for next != myColor {
cond.Wait()
}
// it is my turn, paint
fmt.Printf("%s\n", myColor)
// remove myself from the remaining
var s []string
for _, c := range remaining {
if c == myColor {
continue
}
s = append(s, c)
}
remaining = s
// designate a successor
if len(remaining) > 0 {
next = remaining[rand.Intn(len(remaining))]
}
// release the lock
cond.L.Unlock()
// notify everyone else
cond.Broadcast()
}
func main() {
var painters = make([]string, len(remaining))
copy(painters, remaining)
for _, c := range painters {
go painter(c)
}
// pick a random first painter
next = remaining[rand.Intn(len(remaining))]
cond.Broadcast()
// wait until all painters painted
cond.L.Lock()
for len(remaining) > 0 {
cond.Wait()
}
fmt.Printf("all painters done")
cond.L.Unlock()
}
</syntaxhighlight>

Revision as of 23:59, 20 January 2024

External

Internal

Overview

Cond implements a condition variable, a rendezvous point for goroutines waiting for or announcing the occurrence of an event. In this definition, an "event" is an arbitrary signal between two or more goroutines that carries no information other than the fact that it has occurred. Cond offers a way for a goroutine to "efficiently sleep" (be suspended) until it is signaled to wake up and check of a condition, by other calling Signal() or Broadcast().

For many simple use cases, users will be better off using channels than a Cond (Broadcast() corresponds to closing a channel, and Signal corresponds to sending on a channel).

The typical usage pattern is described below:

Each goroutine that wants to access condition, concurrently, uses this access pattern:

cond.L.Lock()
for !condition() {
  c.Wait() // Wait() releases the lock internally, as a side effect
}

// At this point the goroutine has exclusive access to the condition.

// Make use of the condition, accessing it is concurrently-safe.
...
c.L.Unlock()
// Ensure that other goroutine competing for the condition are woken up with Broadcast() or Signal()

To wake just one goroutine waiting on the condition use Signal().

To wake up all goroutines waiting on the condition use Broadcast().

Each Cond has an associated Locker L, commonly a *Mutex or *RWMutex, which must be held when changing the condition and when calling the Wait method. A Cond must not be copied after first use. In the terminology of the Go memory model, Cond arranges that a call to Broadcast() or Signal() "synchronizes before" any Wait call that it unblocks.

Example

var cond = sync.NewCond(&sync.Mutex{})
var remaining = []string{"red", "green", "blue", "yellow", "black", "fuchsia"}
var next string

func painter(myColor string) {
	cond.L.Lock()
	// wait for my turn, "sleep" otherwise
	for next != myColor {
		cond.Wait()
	}
	// it is my turn, paint
	fmt.Printf("%s\n", myColor)
	// remove myself from the remaining
	var s []string
	for _, c := range remaining {
		if c == myColor {
			continue
		}
		s = append(s, c)
	}
	remaining = s
	// designate a successor
	if len(remaining) > 0 {
		next = remaining[rand.Intn(len(remaining))]
	}
	// release the lock
	cond.L.Unlock()
	// notify everyone else
	cond.Broadcast()
}

func main() {
	var painters = make([]string, len(remaining))
	copy(painters, remaining)
	for _, c := range painters {
		go painter(c)
	}
	// pick a random first painter
	next = remaining[rand.Intn(len(remaining))]
	cond.Broadcast()

	// wait until all painters painted
	cond.L.Lock()
	for len(remaining) > 0 {
		cond.Wait()
	}
	fmt.Printf("all painters done")
	cond.L.Unlock()
}