sync.RWMutex
реализует предпочтительную блокировку как для записи, так и для чтения. Все зависит от того, как вы используете его, чтобы получить преимущество при чтении или записи.
Взяв псевдокод вашей ссылки на википедию в качестве примера Lock-For-Read (в предпочтительной для чтения ситуации):
* Input: mutex m, condition variable c, integer r (number of readers waiting), flag w (writer waiting).
* Lock m (blocking).
* While w:
* wait c, m[a]
* Increment r.
* Unlock m.
И шаблон блокировки для записи в предпочтительной для чтения ситуации, если вы следуете приведенному выше шаблону для блокировки для чтения:
* Lock m (blocking).
* While (w or r > 0):
* wait c, m
* Set w to true.
* Unlock m.
Вы можете увидеть этот механизм в действии в том, как реализован RWMutex
. Помните, что платформа Go — это просто код Go — просмотрите код, чтобы увидеть, как он реализован:
https://golang.org/src/sync/rwmutex.go?s=879:905#L20
29 // RLock locks rw for reading.
30 func (rw *RWMutex) RLock() {
31 if race.Enabled {
32 _ = rw.w.state
33 race.Disable()
34 }
35 if atomic.AddInt32(&rw.readerCount, 1) < 0 {
36 // A writer is pending, wait for it.
37 runtime_Semacquire(&rw.readerSem)
38 }
39 if race.Enabled {
40 race.Enable()
41 race.Acquire(unsafe.Pointer(&rw.readerSem))
42 }
43 }
Ключевым моментом, на который следует обратить внимание, является rw.readerSem
в приведенном выше коде, который дает вам integer r
в примере шаблона википедии, который языки (например, Go и другие) называют семафором:
http://www.golangpatterns.info/concurrency/semaphores
Настоящая суть ожидания находится в строке 37, для runtime_Semaquire()
:
https://golang.org/src/sync/runtime.go
11 // Semacquire waits until *s > 0 and then atomically decrements it.
12 // It is intended as a simple sleep primitive for use by the synchronization
13 // library and should not be used directly.
14 func runtime_Semacquire(s *uint32)
Зная это и видя, как RWMutex.RLock()
увеличивает число чтения, вы можете соответствующим образом реорганизовать свой код.
Взгляните, как RWMutex.RUnlock
уменьшает это, но, что наиболее важно, как RWMutex.Lock()
заставляет ждать всех активных читателей:
71 // Lock locks rw for writing.
72 // If the lock is already locked for reading or writing,
73 // Lock blocks until the lock is available.
74 // To ensure that the lock eventually becomes available,
75 // a blocked Lock call excludes new readers from acquiring
76 // the lock.
77 func (rw *RWMutex) Lock() {
78 if race.Enabled {
79 _ = rw.w.state
80 race.Disable()
81 }
82 // First, resolve competition with other writers.
83 rw.w.Lock()
84 // Announce to readers there is a pending writer.
85 r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
86 // Wait for active readers.
87 if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
88 runtime_Semacquire(&rw.writerSem)
89 }
90 if race.Enabled {
91 race.Enable()
92 race.Acquire(unsafe.Pointer(&rw.readerSem))
93 race.Acquire(unsafe.Pointer(&rw.writerSem))
94 }
95 }
Скорее всего, поэтому вы видите, что второй читатель ждет.
Помните, что семафоры являются общими не только для созданного вами экземпляра RWMutex
, но и во время выполнения для планирования других горутин и других блокировок. Следовательно, попытка принудительного применения шаблона может принести больше вреда, чем пользы в приложении.
Я бы посоветовал сделать шаг назад и подумать, зачем вообще нужна блокировка чтения в вашей архитектуре. Вы действительно на том уровне производительности, при котором переключение контекста ЦП замедляет работу вашего высокочастотного приложения? Я бы сказал, что есть более систематический подход, который можно было бы использовать вместо того, чтобы пытаться реализовать шаблон блокировки для чтения только потому, что он звучит круто и звучит так, как будто он решает все ваши проблемы. Каковы ваши контрольные цифры? Каков размер входных данных и сколько параллельных процессов? Нужно ли делиться? Это меньше X ГБ потребления памяти и можете ли вы переключиться на размещение вещей в стеке (например, каналы, без блокировки мьютекса)? Как насчет чтения данных в стеке и сохранения набора для записи для блокировки? Как долго сборщик мусора не очистит стек, по сравнению с тем, чтобы хранить вещи в куче? и т.д.
person
eduncan911
schedule
11.04.2016