Go by Example: sync.RWMutex

ref: sync.RWMutex Solving readers-writers problems Readers-writers problems occur when shared piece of data needs to be accessed by multiple threads.

package main
import (
    "fmt"
    "math/rand"
    "strings"
    "sync"
    "time"
)
func init() {
    rand.Seed(time.Now().Unix())
}
func sleep() {
    time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
}
func reader(c chan int, m *sync.RWMutex, wg *sync.WaitGroup) {
    sleep()
    m.RLock() // 1) acquire Read lock
    c <- 1    // 2) add acquired lock count
    sleep()   // 3) do something after acquiring lock
    c <- -1   // 4) reduce acquired lock count
    m.RUnlock()
    wg.Done()
}
func writer(c chan int, m *sync.RWMutex, wg *sync.WaitGroup) {
    sleep()
    m.Lock() // 1) acquire Write/Exclusive lock
    c <- 1
    sleep()
    c <- -1
    m.Unlock()
    wg.Done()
}
const READER_COUNT = 10
const WRITER_COUNT = 3
func main() {
    var m sync.RWMutex
    var rs, ws int
    readerCh := make(chan int)
    writerCh := make(chan int)

print out the lock count result

    go func() {
        for {
            select {
            case n := <-readerCh:
                rs += n
            case n := <-writerCh:
                ws += n
            }

simulate concurrent lock acquired count

            fmt.Printf("%s%s\n", strings.Repeat("R", rs),
                strings.Repeat("W", ws))
        }
    }()
    wg := sync.WaitGroup{}
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go reader(readerCh, &m, &wg)
    }
    for i := 0; i < 3; i++ {
        wg.Add(1)
        go writer(writerCh, &m, &wg)
    }
    wg.Wait()
}

New line is printed each time set of goroutines executing critical section changes. It’s visible that RWMutex allows for either 1) at least one reader or 2) exactly one writer.

go run rwmutex.go
W
R
RR
RRR
RRRR
RRRRR
RRRR
RRR
RRRR
RRR
RR
R
W
R
RR
RRR
RRRR
RRR
RR
R
W

Next example: sync.singleflight.