加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

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

发布时间:2020-12-16 09:24:34 所属栏目:大数据 来源:网络整理
导读:我在golang需要一个 read preferring RW互斥锁. golang中是否有满足我需求的包.我试过sync.RWMutex,但似乎是写偏爱锁.我试图区分Go的RWMutex, package mainimport ( "fmt" "sync" "time")func main() {y := resource{x: 10}go func() { defer fmt.Println("d
我在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实现了write preferred和read首选锁定.这一切都取决于你如何使用它来获得写首选或首选.

将您的维基百科链接伪代码作为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的模式,在读取首选sistuation中的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,它为您提供维基百科示例模式中的整数r,哪些语言(如Go和其他语言)调用信号量:

http://www.golangpatterns.info/concurrency/semaphores

对于runtime_Semaquire(),等待的真正含义是在第37行:

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的内存消耗,你可以切换到堆栈上的东西(例如通道,没有互斥锁定)?如何在堆栈上读取数据并保留用于锁定的写入集?有多长时间才能清理堆栈而不必将堆放在堆上?等等

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读