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

Iht*_*kaS 4 concurrency go rwlock

我需要在golang中读取RW mutex.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
}
Run Code Online (Sandbox Code Playgroud)

输出:

first write req
second read req
done first read
after first write granted
done first write
after second read granted
done second read
Run Code Online (Sandbox Code Playgroud)

第二个读者一直等到作家释放锁定.

edu*_*911 7

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.
Run Code Online (Sandbox Code Playgroud)

只要您遵循上面针对Lock-For-Reads的模式,就可以在读取首选的sistuation中执行Lock-For-Write模式:

* Lock m (blocking).
* While (w or r > 0):
* wait c, m
* Set w to true.
* Unlock m.
Run Code Online (Sandbox Code Playgroud)

您可以在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  }
Run Code Online (Sandbox Code Playgroud)

需要注意的一个关键是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)
Run Code Online (Sandbox Code Playgroud)

知道这一点,并看到如何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  }
Run Code Online (Sandbox Code Playgroud)

这很可能是为什么你看到第二个读者等待的原因.

请记住,信号量不仅在RWMutex您创建的实例之间共享,而且在整个运行时中共享,以便在其他goroutine和其他锁定周围进行调度.因此,为什么在应用程序中尝试强制模式可能会带来更多弊大于利.

我的建议是退后一步,考虑为什么你想要在你的架构中首选锁定.您真的处于性能水平,CPU上下文切换会降低您的高频应用程序的速度吗?我会说有一个更系统的方法可以采取而不是试图实现'读取首选锁定'模式只是因为它听起来很酷,听起来像它解决了你所有的问题.你的基准数字是多少?输入数据的大小是多少,以及并发进程的数量是多少?是否必须共享?它是否低于X GB的内存消耗,你可以切换到堆栈上的东西(例如通道,没有互斥锁定)?如何在堆栈上读取数据并保留用于锁定的写入集?有多长时间才能清理堆栈而不必将堆放在堆上?等等