我需要一个 golang 中的 read preferring RW 互斥锁。 golang中是否有满足我需求的包。我试过sync.RWMutex,但它似乎是写优先锁。这是我试图区分 Go 的 RWMutex,
package main
import (
"fmt"
"sync"
"time"
)
func main() {
y := &resource{x: 10}
go func() {
defer fmt.Println("done first read")
y.RLock()
defer y.RUnlock()
go func() {
defer fmt.Println("done first write")
fmt.Println("first write req")
y.Lock()
fmt.Println("after first write granted")
defer y.Unlock()
}()
time.Sleep(time.Second)
go func() {
defer fmt.Println("done second read")
fmt.Println("second read req")
y.RLock()
fmt.Println("after second read granted")
defer y.RUnlock()
}()
time.Sleep(10 * time.Second)
}()
time.Sleep(time.Minute)
}
type resource struct {
sync.RWMutex
x int
}
输出:
first write req
second read req
done first read
after first write granted
done first write
after second read granted
done second read
第二个读者一直等待直到作者释放锁。
最佳答案
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 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 的内存消耗,您是否可以切换到将内容放在堆栈上(例如 channel ,没有互斥锁)?堆栈上的读取数据如何并保留一个写集用于锁定? GC 清理堆栈与必须将内容保留在堆上需要多长时间?等等等等。
关于go - 在 Golang 中阅读更喜欢的 RW 互斥锁,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/36548702/