Resolve "add rwmutex"
This commit is contained in:
parent
39a54dbee4
commit
1c237114cd
3 changed files with 212 additions and 0 deletions
41
rwmutex/README.md
Normal file
41
rwmutex/README.md
Normal file
|
@ -0,0 +1,41 @@
|
|||
## rwmutex
|
||||
|
||||
[sync.RWMutex](https://golang.org/pkg/sync/#RWMutex) -- это примитив синхронизации,
|
||||
предоставляющий доступ к критической секции произвольному количеству читателей,
|
||||
не более, чем одному читателю. При этом, если есть писатель, то читателей нет.
|
||||
|
||||
### Что нужно сделать?
|
||||
|
||||
Нужно написать реализацию RWMutex используя каналы.
|
||||
|
||||
Использование пакета [sync](https://golang.org/pkg/sync) в этой задаче запрещено!
|
||||
|
||||
```go
|
||||
type RWMutex struct {}
|
||||
|
||||
func (rw *RWMutex) Lock() {}
|
||||
func (rw *RWMutex) Unlock() {}
|
||||
|
||||
func (rw *RWMutex) RLock() {}
|
||||
func (rw *RWMutex) RUnlock() {}
|
||||
```
|
||||
|
||||
`RWMutex` можно представлять себе как две блокировки, блокировка на чтение и блокировка на запись.
|
||||
|
||||
`New()` возвращает `RWMutex`, в котором ни одна из блокировок не взята.
|
||||
|
||||
Процессы, желающие изменить данные (писатели), берут блокировку на запись с помощью метода `Lock`.
|
||||
Процессы, желающие прочитать данные (читатели), берут блокировку на чтение с помощью метода `RLock`.
|
||||
По окончании записи писатель отпускает блокировку на запись (`Unlock`).
|
||||
С блокировкой на чтение связано число, число активных читателей.
|
||||
При взятии блокировки (`RLock`) это число инкрементируется.
|
||||
При завершении чтения (`RUnlock`) блокировка на чтение уменьшает счётчик.
|
||||
|
||||
#### Свойства
|
||||
1. Писатель не заблокируется при взятии блокировки только при условии,
|
||||
что никакой другой писатель и никакой другой читатель не владеет соответствующей блокировкой.
|
||||
2. Если какой-то писатель взял блокировку на запись, любой новый писатель или читатель заблокируется при взятии блокировки.
|
||||
3. Если какой-то читатель взял блокировку на чтение, любой новый писатель заблокируется на взятии блокировки.
|
||||
Однако любой писатель сможет взять блокировку на чтение.
|
||||
|
||||
Для выполнения этих свойств достаточно двух каналов.
|
55
rwmutex/rwmutex.go
Normal file
55
rwmutex/rwmutex.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
// +build !solution
|
||||
|
||||
package rwmutex
|
||||
|
||||
// A RWMutex is a reader/writer mutual exclusion lock.
|
||||
// The lock can be held by an arbitrary number of readers or a single writer.
|
||||
// The zero value for a RWMutex is an unlocked mutex.
|
||||
//
|
||||
// If a goroutine holds a RWMutex for reading and another goroutine might
|
||||
// call Lock, no goroutine should expect to be able to acquire a read lock
|
||||
// until the initial read lock is released. In particular, this prohibits
|
||||
// recursive read locking. This is to ensure that the lock eventually becomes
|
||||
// available; a blocked Lock call excludes new readers from acquiring the
|
||||
// lock.
|
||||
type RWMutex struct {
|
||||
}
|
||||
|
||||
// New creates *RWMutex.
|
||||
func New() *RWMutex {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RLock locks rw for reading.
|
||||
//
|
||||
// It should not be used for recursive read locking; a blocked Lock
|
||||
// call excludes new readers from acquiring the lock. See the
|
||||
// documentation on the RWMutex type.
|
||||
func (rw *RWMutex) RLock() {
|
||||
|
||||
}
|
||||
|
||||
// RUnlock undoes a single RLock call;
|
||||
// it does not affect other simultaneous readers.
|
||||
// It is a run-time error if rw is not locked for reading
|
||||
// on entry to RUnlock.
|
||||
func (rw *RWMutex) RUnlock() {
|
||||
|
||||
}
|
||||
|
||||
// Lock locks rw for writing.
|
||||
// If the lock is already locked for reading or writing,
|
||||
// Lock blocks until the lock is available.
|
||||
func (rw *RWMutex) Lock() {
|
||||
|
||||
}
|
||||
|
||||
// Unlock unlocks rw for writing. It is a run-time error if rw is
|
||||
// not locked for writing on entry to Unlock.
|
||||
//
|
||||
// As with Mutexes, a locked RWMutex is not associated with a particular
|
||||
// goroutine. One goroutine may RLock (Lock) a RWMutex and then
|
||||
// arrange for another goroutine to RUnlock (Unlock) it.
|
||||
func (rw *RWMutex) Unlock() {
|
||||
|
||||
}
|
116
rwmutex/rwmutex_test.go
Normal file
116
rwmutex/rwmutex_test.go
Normal file
|
@ -0,0 +1,116 @@
|
|||
package rwmutex
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func parallelReader(m *RWMutex, clocked, cunlock, cdone chan bool) {
|
||||
m.RLock()
|
||||
clocked <- true
|
||||
<-cunlock
|
||||
m.RUnlock()
|
||||
cdone <- true
|
||||
}
|
||||
|
||||
func doTestParallelReaders(numReaders, gomaxprocs int) {
|
||||
runtime.GOMAXPROCS(gomaxprocs)
|
||||
m := New()
|
||||
clocked := make(chan bool)
|
||||
cunlock := make(chan bool)
|
||||
cdone := make(chan bool)
|
||||
for i := 0; i < numReaders; i++ {
|
||||
go parallelReader(m, clocked, cunlock, cdone)
|
||||
}
|
||||
// Wait for all parallel RLock()s to succeed.
|
||||
for i := 0; i < numReaders; i++ {
|
||||
<-clocked
|
||||
}
|
||||
for i := 0; i < numReaders; i++ {
|
||||
cunlock <- true
|
||||
}
|
||||
// Wait for the goroutines to finish.
|
||||
for i := 0; i < numReaders; i++ {
|
||||
<-cdone
|
||||
}
|
||||
}
|
||||
|
||||
func TestParallelReaders(t *testing.T) {
|
||||
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1))
|
||||
doTestParallelReaders(1, 4)
|
||||
doTestParallelReaders(3, 4)
|
||||
doTestParallelReaders(4, 2)
|
||||
}
|
||||
|
||||
func reader(rwm *RWMutex, numIterations int, activity *int32, cdone chan bool) {
|
||||
for i := 0; i < numIterations; i++ {
|
||||
rwm.RLock()
|
||||
n := atomic.AddInt32(activity, 1)
|
||||
if n < 1 || n >= 10000 {
|
||||
rwm.RUnlock()
|
||||
panic(fmt.Sprintf("wlock(%d)\n", n))
|
||||
}
|
||||
for i := 0; i < 100; i++ {
|
||||
}
|
||||
atomic.AddInt32(activity, -1)
|
||||
rwm.RUnlock()
|
||||
}
|
||||
cdone <- true
|
||||
}
|
||||
|
||||
func writer(rwm *RWMutex, numIterations int, activity *int32, cdone chan bool) {
|
||||
for i := 0; i < numIterations; i++ {
|
||||
rwm.Lock()
|
||||
n := atomic.AddInt32(activity, 10000)
|
||||
if n != 10000 {
|
||||
rwm.Unlock()
|
||||
panic(fmt.Sprintf("wlock(%d)\n", n))
|
||||
}
|
||||
for i := 0; i < 100; i++ {
|
||||
}
|
||||
atomic.AddInt32(activity, -10000)
|
||||
rwm.Unlock()
|
||||
}
|
||||
cdone <- true
|
||||
}
|
||||
|
||||
func HammerRWMutex(gomaxprocs, numReaders, numIterations int) {
|
||||
runtime.GOMAXPROCS(gomaxprocs)
|
||||
// Number of active readers + 10000 * number of active writers.
|
||||
var activity int32
|
||||
rwm := New()
|
||||
cdone := make(chan bool)
|
||||
go writer(rwm, numIterations, &activity, cdone)
|
||||
var i int
|
||||
for i = 0; i < numReaders/2; i++ {
|
||||
go reader(rwm, numIterations, &activity, cdone)
|
||||
}
|
||||
go writer(rwm, numIterations, &activity, cdone)
|
||||
for ; i < numReaders; i++ {
|
||||
go reader(rwm, numIterations, &activity, cdone)
|
||||
}
|
||||
// Wait for the 2 writers and all readers to finish.
|
||||
for i := 0; i < 2+numReaders; i++ {
|
||||
<-cdone
|
||||
}
|
||||
}
|
||||
|
||||
func TestRWMutex(t *testing.T) {
|
||||
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1))
|
||||
n := 1000
|
||||
if testing.Short() {
|
||||
n = 5
|
||||
}
|
||||
HammerRWMutex(1, 1, n)
|
||||
HammerRWMutex(1, 3, n)
|
||||
HammerRWMutex(1, 10, n)
|
||||
HammerRWMutex(4, 1, n)
|
||||
HammerRWMutex(4, 3, n)
|
||||
HammerRWMutex(4, 10, n)
|
||||
HammerRWMutex(10, 1, n)
|
||||
HammerRWMutex(10, 3, n)
|
||||
HammerRWMutex(10, 10, n)
|
||||
HammerRWMutex(10, 5, n)
|
||||
}
|
Loading…
Reference in a new issue