5

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

2 番目のリーダーは、ライターがロックを解除するまで待機します。

4

2 に答える 2

8

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.

また、上記の Lock-For-Reads のパターンに従っている限り、Read-preferred situation での 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  }

これが、2 番目のリーダーが待機している理由である可能性が最も高いです。

セマフォは、RWMutex作成したインスタンスだけでなく、ランタイム全体で共有され、他のゴルーチンや他のロックをスケジュールすることを忘れないでください。したがって、パターンを強制しようとすると、アプリ全体に害が及ぶ可能性があるのはなぜですか。

私のアドバイスは、一歩下がって、アーキテクチャで読み取り優先ロックが必要な理由を検討することです。CPU コンテキストの切り替えが高頻度アプリケーションの速度を低下させるパフォーマンス レベルに本当に達していますか? 「読み取り優先ロック」パターンを実装しようとする代わりに、より体系的なアプローチがあると思います。それがクールに聞こえ、すべての問題を解決するように聞こえるからです。ベンチマークの数値は?入力データのサイズと同時処理数は? 共有する必要がありますか?メモリ消費量が X GB 未満で、スタック (チャネル、ミューテックスロックなし)?スタック上の読み取りデータとロック用の書き込みセットを保持するのはどうですか? GC がスタックをクリーンアップするまでの時間と、ヒープに物を保持しなければならない時間はどれくらいですか? などなど

于 2016-04-11T17:59:54.063 に答える