Свой RWMutex: читаем и пишем с приоритетом на чтение
Условие задачи
Реализуй упрощённую версию 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()
}