読み書きロックの奇妙な表現


先週、パフォーマンスのチューニングをしたとき、同時読み書きを測定するシーンデータがおかしいことに気づきました.
シーンは1つの書き込みスレッドに異なる数の読み取りスレッドを加えたときの読み書きQPSを測定した結果、データは大体次のようになった.
           QPS     QPS
1          1          4000     40
1          5          3000     10000
1          10         3000     20000
...

コードは大体こうです.
//  
    ReadWriteLockGuard lock(mLock, 'w');
    // do something...

//  
    ReadWriteLockGuard lock(mLock, 'r');
    // do something...

このコードから見ると、読み書きスレッドが1:1の場合、2つのスレッドが順番にロックを奪うべきだが、QPSは書き込みスレッドがロックを奪う回数がリードスレッドの100倍であることを示している.
そこで私は読み書きスレッドのコードに1行の印刷を加えて、読み書きスレッドがロックを奪う状況を見ました.
//  
    ReadWriteLockGuard lock(mLock, 'w');
    // do something...
    cout << "w" << endl;

//  
    ReadWriteLockGuard lock(mLock, 'r');
    // do something...
    cout << "r" << endl;

結果は予想外でした.
r
w
w
w
...
w
r
w
w
...

とにかく書き込みスレッドが何回もロックを奪った後、かわいそうな読み取りスレッドが1回ロックを奪った.
奇妙な現象.
私は以前、読み書きロックのロックフローについて理解していました.
  • 現在スレッドがロックを持っていない場合、最初のロックを奪うアクティブなスレッドはロックを取得します.
  • 現在の所有者がロックを解除すると、キューに並んでいるすべてのスレッドがロックを奪う.
  • キューに並んでいるスレッドの中に書き込みロック待ちのスレッドがある場合、読み込みロックを申請するとブロックされます(書き込みロック優先).

  • 今から見ればこの理解には問題があり、この現象を説明することはできない.
    同僚と交流してみると、読み書きロックのロックフローはこうかもしれません.
  • スレッドがロックブロックを申請した場合、まずSpinLockを呼び出し、その後もロックが奪われなかった場合、カーネルはそれをスリープ状態に設定し、待機キューに参加する.
  • 現在スレッドを保持してロックを解除すると、カーネルはすべての待機キューのスリープスレッドを起動し、スケジューリングキューに参加する.
  • スケジューリングキューに入った競合スレッドは、スケジューリングされた実行後、ロックを開始する.

  • このプロセスから見ると、私が遭遇した状況はこのように説明できます.
  • リードスレッドが最初に実行され、ロックが奪われた.
  • 書き込み優先なので、リードスレッドが終了するとロックは書き込みスレッドに譲るに違いない.
  • 書き込みスレッドがロックを解除した後、読み取りスレッドが起動され、この時も待機状態にあり、実行されず、ロックを奪うことができない.
  • 書き込みスレッドは睡眠がなく、再びロックを奪い、この時書き込み優先の影響がなく、ロックを奪い取ることに成功した.
  • リードスレッドが実行を開始し、ロックに失敗し、再睡眠しました.

  • リードスレッドが多くなると,書き込みスレッドがロックを解除すると必ずしもロックを奪うことができなくなるため,一定の時間睡眠をとることができ,これによりリードスレッドがロックを奪う確率がさらに増大するため,読み取りのQPSが急上昇することが観察される.
    上の推測はまだ検証されていません.暇があればpthreadを見なければなりません.read_write_ロックの実現ですね.