物語
どこかから定期的にデータを収集するライタースレッドがあります(リアルタイムですが、問題ではそれほど重要ではありません)。これらのデータから読み取る多くのリーダーがあります。これに対する通常の解決策は、次のように 2 つのリーダー/ライターのロックと 2 つのバッファーを使用することです。
Writer (case 1):
acquire lock 0
loop
write to current buffer
acquire other lock
free this lock
swap buffers
wait for next period
または
Writer (case 2):
acquire lock 0
loop
acquire other lock
free this lock
swap buffers
write to current buffer
wait for next period
問題
どちらの方法でも、他のロックの取得操作が失敗した場合、スワップは行われず、ライターは以前のデータを上書きします (ライターはリアルタイムであるため、リーダーを待つことができないため)。したがって、この場合、すべてのリーダーはそのフレームを失います。データの。
これは大したことではありませんが、リーダーは私自身のコードであり、短いので、ダブル バッファーを使用すると、この問題は解決されます。問題が発生した場合は、トリプル バッファー (またはそれ以上) にすることができます。
問題は、最小限にしたい遅延です。ケース 1 を想像してください。
writer writes to buffer0 reader is reading buffer1
writer can't acquire lock1 because reader is still reading buffer1
| |
| reader finishes reading,
| (writer waiting for next period) <- **this point**
|
|
writer wakes up, and again writes to buffer0
**この時点**で、理論的にはbuffer0
、次の期間を待つのではなく、リーダーが終了した後にライターだけがスワップを実行できれば、他のリーダーはデータを読み取ることができたはずです。この場合、1 つのリーダーが少し遅れただけで、すべてのリーダーが 1 フレームのデータを見逃していましたが、問題は完全に回避できたはずです。
ケース 2 も同様です。
writer writes to buffer0 reader is idle
| |
| reader finishes reading,
| (writer waiting for next period)
|
| reader starts reading buffer1
writer wakes up |
it can't acquire lock1 because reader is still reading buffer1
overwrites buffer0
解決策を混ぜてみたので、ライターは書き込み直後にバッファーのスワップを試み、それができない場合は次のピリオドでウェイクアップした直後に試行します。だから、このようなもの:
Writer (case 3):
acquire lock 0
loop
if last buffer swap failed
acquire other lock
free this lock
swap buffers
write to current buffer
acquire other lock
free this lock
swap buffers
wait for next period
遅延の問題はまだ残っています:
writer writes to buffer0 reader is reading buffer1
writer can't acquire lock1 because reader is still reading buffer1
| |
| reader finishes reading,
| (writer waiting for next period) <- **this point**
|
|
writer wakes up
swaps buffers
writes to buffer1
再び **この時点** で、すべてのリーダーが を読み始めることができますbuffer0
。これは、 が書き込まれてから少し遅れbuffer0
ますが、代わりに、ライターの次のピリオドまで待たなければなりません。
質問
問題は、これをどのように処理するかです。希望の周期で正確にライターを実行させたい場合は、RTAI関数を使用してその周期を待つ必要があり、私はそれを行うことができません
Writer (case 4):
acquire lock 0
loop
write to current buffer
loop a few times or until the buffer has been swapped
sleep a little
acquire other lock
free this lock
swap buffers
wait for next period
これにより、ジッターが発生します。「数回」が「次の期間を待つ」よりも長くなる可能性があるため、ライターはその期間の開始を見逃す可能性があります。
より明確にするために、ここに私がしたいことがあります:
writer writes to buffer0 reader is reading buffer1
| |
| reader finishes reading,
| (writer waiting for next period) As soon as all readers finish reading,
| the buffer is swapped
| readers start reading buffer0
writer wakes up |
writes to buffer1
私がすでに見つけたもの
私が理解している限り、バッファーにメモリを割り当て続け、リーダーがそれらを使い果たすまでそれらを解放するread-copy-updateを見つけましたが、これは多くの理由で私には不可能です。1 つは、スレッドがカーネルとユーザー空間の間で共有されることです。次に、RTAI を使用すると、リアルタイム スレッドでメモリを割り当てることができません (スレッドが Linux のシステム コールを呼び出すことになり、リアルタイム性が損なわれるためです!)同じ理由で)
また、より高い頻度でバッファーのスワップを試行する追加のスレッドを用意することも考えましたが、それはあまり良い考えではないように思えます。まず、それ自体がライターと同期する必要があります。次に、これらのライター/リーダーの多くがさまざまな部分で並行して動作しているため、ライターごとに 1 つの余分なスレッドが多すぎるように思われます。すべてのライターに対して 1 つのスレッドというのは、各ライターとの同期に関して非常に複雑に思えます。