为了账号安全,请及时绑定邮箱和手机立即绑定

在 Golang 中阅读更喜欢的 RW 互斥锁

在 Golang 中阅读更喜欢的 RW 互斥锁

Go
Cats萌萌 2022-01-04 10:45:37
我需要阅读更喜欢golang 中的RW互斥锁。golang中是否有满足我需求的包。我试过sync.RWMutex,但它似乎是写优先锁。这是我试图区分 Go 的 RWMutex,package mainimport (    "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 reqsecond read reqdone first readafter first write granteddone first writeafter second read granteddone second read第二个读者一直等待直到作者释放锁。
查看完整描述

2 回答

?
慕桂英4014372

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 清理堆栈需要多长时间,而不必将内容保留在堆上?等等等等。


查看完整回答
反对 回复 2022-01-04
?
九州编程

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


查看完整回答
反对 回复 2022-01-04
  • 2 回答
  • 0 关注
  • 149 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号