Свой RWMutex: читаем и пишем с приоритетом на чтение

К задачам
Сложная
Concurrency

Условие задачи

Реализуй упрощённую версию RWMutex с приоритетом на чтение. Твоя задача — позволить нескольким горутинам читать одновременно, но запись должна происходить эксклюзивно. Нельзя использовать sync.RWMutex — только sync.Mutex и sync.Cond.

package main

import (
    "sync"
)

type MyRWMutex struct {
    mu      sync.Mutex
    readers int
    writing bool
    cond    *sync.Cond
}

func NewMyRWMutex() *MyRWMutex {
    m := &MyRWMutex{}
    m.cond = sync.NewCond(&m.mu)
    return m
}

func (m *MyRWMutex) RLock() {
    // TODO
}

func (m *MyRWMutex) RUnlock() {
    // TODO
}

func (m *MyRWMutex) Lock() {
    // TODO
}

func (m *MyRWMutex) Unlock() {
    // TODO
}

func main() {
    // Протестируй свою реализацию
}
package main

import (
    "sync"
)

type MyRWMutex struct {
    mu      sync.Mutex
    readers int
    writing bool
    cond    *sync.Cond
}

func NewMyRWMutex() *MyRWMutex {
    m := &MyRWMutex{}
    m.cond = sync.NewCond(&m.mu)
    return m
}

func (m *MyRWMutex) RLock() {
    // TODO
}

func (m *MyRWMutex) RUnlock() {
    // TODO
}

func (m *MyRWMutex) Lock() {
    // TODO
}

func (m *MyRWMutex) Unlock() {
    // TODO
}

func main() {
    // Протестируй свою реализацию
}

Подсказка

- Чтение допускается, если нет writer'а. - Писатель ждёт, пока не завершатся все reader'ы. - Используй sync.Cond для ожидания условий.

Решение

Ключевая идея — использовать счётчик активных reader'ов и флаг writing. Cond позволяет "усыплять" писателя до тех пор, пока можно безопасно войти.

package main

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

type MyRWMutex struct {
    mu      sync.Mutex
    readers int
    writing bool
    cond    *sync.Cond
}

func NewMyRWMutex() *MyRWMutex {
    m := &MyRWMutex{}
    m.cond = sync.NewCond(&m.mu)
    return m
}

func (m *MyRWMutex) RLock() {
    m.mu.Lock()
    for m.writing {
        m.cond.Wait()
    }
    m.readers++
    m.mu.Unlock()
}

func (m *MyRWMutex) RUnlock() {
    m.mu.Lock()
    m.readers--
    if m.readers == 0 {
        m.cond.Broadcast()
    }
    m.mu.Unlock()
}

func (m *MyRWMutex) Lock() {
    m.mu.Lock()
    for m.writing || m.readers > 0 {
        m.cond.Wait()
    }
    m.writing = true
    m.mu.Unlock()
}

func (m *MyRWMutex) Unlock() {
    m.mu.Lock()
    m.writing = false
    m.cond.Broadcast()
    m.mu.Unlock()
}

func main() {
    mu := NewMyRWMutex()
    var wg sync.WaitGroup

    for i := 0; i < 3; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            mu.RLock()
            fmt.Println("Reader", id, "started")
            time.Sleep(100 * time.Millisecond)
            fmt.Println("Reader", id, "finished")
            mu.RUnlock()
        }(i)
    }

    time.Sleep(50 * time.Millisecond)

    wg.Add(1)
    go func() {
        defer wg.Done()
        mu.Lock()
        fmt.Println("Writer started")
        time.Sleep(200 * time.Millisecond)
        fmt.Println("Writer finished")
        mu.Unlock()
    }()

    wg.Wait()
}
package main

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

type MyRWMutex struct {
    mu      sync.Mutex
    readers int
    writing bool
    cond    *sync.Cond
}

func NewMyRWMutex() *MyRWMutex {
    m := &MyRWMutex{}
    m.cond = sync.NewCond(&m.mu)
    return m
}

func (m *MyRWMutex) RLock() {
    m.mu.Lock()
    for m.writing {
        m.cond.Wait()
    }
    m.readers++
    m.mu.Unlock()
}

func (m *MyRWMutex) RUnlock() {
    m.mu.Lock()
    m.readers--
    if m.readers == 0 {
        m.cond.Broadcast()
    }
    m.mu.Unlock()
}

func (m *MyRWMutex) Lock() {
    m.mu.Lock()
    for m.writing || m.readers > 0 {
        m.cond.Wait()
    }
    m.writing = true
    m.mu.Unlock()
}

func (m *MyRWMutex) Unlock() {
    m.mu.Lock()
    m.writing = false
    m.cond.Broadcast()
    m.mu.Unlock()
}

func main() {
    mu := NewMyRWMutex()
    var wg sync.WaitGroup

    for i := 0; i < 3; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            mu.RLock()
            fmt.Println("Reader", id, "started")
            time.Sleep(100 * time.Millisecond)
            fmt.Println("Reader", id, "finished")
            mu.RUnlock()
        }(i)
    }

    time.Sleep(50 * time.Millisecond)

    wg.Add(1)
    go func() {
        defer wg.Done()
        mu.Lock()
        fmt.Println("Writer started")
        time.Sleep(200 * time.Millisecond)
        fmt.Println("Writer finished")
        mu.Unlock()
    }()

    wg.Wait()
}