Cond
- Condition Variable is one of the synchronization mechanisms.
- A condistion variable is basically a container of Goroutines that are waiting for a certain condition.
- Condition Variable is used to synchronise execution of goroutines.
How to make a goroutine wait till some event(condition) occur?
Wait in a loop for thecondition
var shareRsc = make(map[string]string)
go func(){
defer wg.Done()
mu.Lock()
for len(sharedRsc) == 0 {
mu.Unlock()
time.Sleep(100*time.Millisecond)
mu.Lock()
}
// Do processing
fmt.Println(sharedRsc["rsc"])
mu.Unlock()
}()
- We need some way to make goroutine suspend while waiting.
- We need some way to singal the suspended goroutine when particular event has occured.
Channels?
- We can use channels to block a goroutine on receive.
- Sender goroutine to indicate occurence of event.
What if there are multiple goroutines waiting on multiple conditions/event?
sync.Cond
var c *sync.Cond
We use constructor method sync.NewCond() to create a conditional variable, it takes sync.Locker interface as input, which is usually sync.Mutex.
m := sync.Mutex{}
c := sync.NewCond(&m)
3 methods
c.Wait() , c.Signal(), c.Broadcast()
c.Wait()
c.L.Lock()
for !condition(){
c.Wait()
}
// ... make use of condition ...
c.L.Unlock()
- suspend execution of the calling goroutine.
- automatically unlocks c.L
Wait()cannot return unless awoken by Broadcast or Signal- Wait locks c.L befor returning.
- Beacuse c.L is not locked when Wait first resumes, the caller typically cannot assume that the condition is true when Wait returns. Instead, the caller should Wait in a loop.
c.Signal()
func (c *Cond) Signal()
- Signal wakes one goroutine waiting on c, if there is any.
- Signal finds goroutine that has been waiting the longest and notifies that.
- It is allowed but not required for the caller to hold c.L during the call

exercises
https://go.dev/play/p/8dQ8wE9BkdR
package main
import (
"fmt"
"sync"
"time"
)
var sharedRsc = make(map[string]interface{})
func main() {
var wg sync.WaitGroup
m := sync.Mutex{}
c := sync.NewCond(&m)
wg.Add(1)
go func() {
defer wg.Done()
// suspend goroutine until sharedRsc is populated.
c.L.Lock()
for len(sharedRsc) == 0 {
c.Wait() // 這邊會等待 c.Signal
}
fmt.Println(sharedRsc["rsc1"])
c.L.Unlock()
}()
// writes changes to sharedRsc
time.Sleep(2 * time.Second)
c.L.Lock()
sharedRsc["rsc1"] = "foo"
c.Signal()
c.L.Unlock()
wg.Wait()
}
c.Broadcast()
func (c *Cond) Broadcast()
- Broadcast wakes all goroutines waiting on c.
- It is allowed but not required for the caller to hold c.L during the call.

exercises
https://go.dev/play/p/IF4AM9Dt6QF
package main
import (
"fmt"
"sync"
)
var sharedRsc = make(map[string]interface{})
func main() {
var wg sync.WaitGroup
m := sync.Mutex{}
c := sync.NewCond(&m)
wg.Add(1)
go func() {
defer wg.Done()
// suspend goroutine until sharedRsc is populated.
c.L.Lock()
for len(sharedRsc) == 0 {
c.Wait()
}
fmt.Println(sharedRsc["rsc1"])
c.L.Unlock()
}()
wg.Add(1)
go func() {
defer wg.Done()
// suspend goroutine until sharedRsc is populated.
c.L.Lock()
for len(sharedRsc) == 0 {
c.Wait()
}
fmt.Println(sharedRsc["rsc2"])
c.L.Unlock()
}()
// writes changes to sharedRsc
c.L.Lock()
sharedRsc["rsc1"] = "foo"
sharedRsc["rsc2"] = "bar"
c.Broadcast()
c.L.Unlock()
wg.Wait()
}