2 回答

TA貢獻1871條經驗 獲得超13個贊
sync.RWMutex實現了寫首選和讀首選鎖定。這完全取決于您如何使用它來獲得首選寫入或首選讀取。
以您的 wikipedia 鏈接偽代碼為例,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-For-Reads 模式,就可以在首選讀取的情況下使用 Lock-For-Write 模式:
* 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您創建的實例之間共享,還在整個運行時共享,以圍繞其他 goroutine 和其他鎖進行調度。因此,為什么在應用程序中嘗試強制模式可能弊大于利。
我的建議是退后一步,考慮一下為什么要在架構中使用讀取優先鎖定。您是否真的處于 CPU 上下文切換會減慢您的高頻應用程序的性能級別?我想說有一個更系統的方法可以被采用,而不是僅僅因為它聽起來很酷并且聽起來它解決了你所有的問題而試圖實現一個“讀取優先鎖定”模式。你的基準數字是多少?輸入數據的大小是多少,以及跨越多少個并發進程?它必須共享嗎?它是否低于 X GB 的內存消耗,您是否可以切換到將內容放在堆棧上(例如通道、沒有互斥鎖)?堆棧上的讀取數據并保留一個寫集用于鎖定呢?GC 清理堆棧需要多長時間,而不必將內容保留在堆上?等等等等。

TA貢獻1785條經驗 獲得超4個贊
似乎可以實現與期望的行為sync.WaitGroup 同步原語,例如
var wg sync.WaitGroup
go func() {
defer fmt.Println("done second read")
fmt.Println("second read req")
y.RLock() //wait writer
wg.Add(1) //report busy
fmt.Println("after second read granted")
defer wg.Done() //report done
defer y.RUnlock()
}()
//forcing writer to wait all readers
go func() {
defer fmt.Println("done first write")
fmt.Println("first write req")
wg.Wait() //wait all readers
y.Lock()
fmt.Println("after first write granted")
defer y.Unlock()
}()
你可以試試https://play.golang.org/p/y831xIrglj
- 2 回答
- 0 關注
- 175 瀏覽
添加回答
舉報