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 Image
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. Image
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()
}
© Kimi Tsai all right reserved.            Updated : 2023-07-12 09:04:54

results matching ""

    No results matching ""

    results matching ""

      No results matching ""