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

我需要阅读更喜欢golang 中的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

第二个读者一直等待直到作者释放锁。


Cats萌萌
浏览 150回答 2
2回答

慕桂英4014372

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#L2029&nbsp; // RLock locks rw for reading.30&nbsp; func (rw *RWMutex) RLock() {31&nbsp; &nbsp; &nbsp; if race.Enabled {32&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _ = rw.w.state33&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; race.Disable()34&nbsp; &nbsp; &nbsp; }35&nbsp; &nbsp; &nbsp; if atomic.AddInt32(&rw.readerCount, 1) < 0 {36&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // A writer is pending, wait for it.37&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; runtime_Semacquire(&rw.readerSem)38&nbsp; &nbsp; &nbsp; }39&nbsp; &nbsp; &nbsp; if race.Enabled {40&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; race.Enable()41&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; race.Acquire(unsafe.Pointer(&rw.readerSem))42&nbsp; &nbsp; &nbsp; }43&nbsp; }需要注意的一个关键是rw.readerSem在上面的代码中,它为您integer r提供了维基百科示例模式,哪些语言(如 Go 和其他语言)称为信号量:http://www.golangpatterns.info/concurrency/semaphores真正的等待在第 37 行,对于runtime_Semaquire():https://golang.org/src/sync/runtime.go11&nbsp; // Semacquire waits until *s > 0 and then atomically decrements it.12&nbsp; // It is intended as a simple sleep primitive for use by the synchronization13&nbsp; // library and should not be used directly.14&nbsp; func runtime_Semacquire(s *uint32)知道了这一点,并看到了如何RWMutex.RLock()递增读取该数字,您可以相应地重构您的代码。看看它是如何RWMutex.RUnlock递减的,但最重要的是如何RWMutex.Lock()强制等待所有活跃的读者:71&nbsp; // Lock locks rw for writing.72&nbsp; // If the lock is already locked for reading or writing,73&nbsp; // Lock blocks until the lock is available.74&nbsp; // To ensure that the lock eventually becomes available,75&nbsp; // a blocked Lock call excludes new readers from acquiring76&nbsp; // the lock.77&nbsp; func (rw *RWMutex) Lock() {78&nbsp; &nbsp; &nbsp; if race.Enabled {79&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _ = rw.w.state80&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; race.Disable()81&nbsp; &nbsp; &nbsp; }82&nbsp; &nbsp; &nbsp; // First, resolve competition with other writers.83&nbsp; &nbsp; &nbsp; rw.w.Lock()84&nbsp; &nbsp; &nbsp; // Announce to readers there is a pending writer.85&nbsp; &nbsp; &nbsp; r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders86&nbsp; &nbsp; &nbsp; // Wait for active readers.87&nbsp; &nbsp; &nbsp; if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {88&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; runtime_Semacquire(&rw.writerSem)89&nbsp; &nbsp; &nbsp; }90&nbsp; &nbsp; &nbsp; if race.Enabled {91&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; race.Enable()92&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; race.Acquire(unsafe.Pointer(&rw.readerSem))93&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; race.Acquire(unsafe.Pointer(&rw.writerSem))94&nbsp; &nbsp; &nbsp; }95&nbsp; }这很可能就是您看到第二个读者在等待的原因。请记住,信号量不仅在RWMutex您创建的实例之间共享,还在整个运行时共享,以围绕其他 goroutine 和其他锁进行调度。因此,为什么在应用程序中尝试强制模式可能弊大于利。我的建议是退后一步,考虑一下为什么要在架构中使用读取优先锁定。您是否真的处于 CPU 上下文切换会减慢您的高频应用程序的性能级别?我想说有一个更系统的方法可以被采用,而不是仅仅因为它听起来很酷并且听起来它解决了你所有的问题而试图实现一个“读取优先锁定”模式。你的基准数字是多少?输入数据的大小是多少,以及跨越多少个并发进程?它必须共享吗?它是否低于 X GB 的内存消耗,您是否可以切换到将内容放在堆栈上(例如通道、没有互斥锁)?堆栈上的读取数据并保留一个写集用于锁定呢?GC 清理堆栈需要多长时间,而不必将内容保留在堆上?等等等等。

九州编程

似乎可以实现与期望的行为sync.WaitGroup 同步原语,例如var wg sync.WaitGroupgo func() {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; defer fmt.Println("done second read")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("second read req")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; y.RLock()&nbsp; &nbsp;//wait writer&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; wg.Add(1)&nbsp; &nbsp;//report busy&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("after second read granted")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; defer wg.Done() //report done&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; defer y.RUnlock()&nbsp; &nbsp; &nbsp; &nbsp; }()//forcing writer to wait all readersgo func() {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; defer fmt.Println("done first write")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("first write req")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; wg.Wait()&nbsp; //wait all readers&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; y.Lock()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("after first write granted")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; defer y.Unlock()&nbsp; &nbsp; &nbsp; &nbsp; }()你可以试试https://play.golang.org/p/y831xIrglj
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go