История
Существует поток записи, периодически собирающий данные откуда-то (в режиме реального времени, но это не имеет большого значения в вопросе). Многие читатели читают эти данные. Обычное решение для этого — с двумя блокировками чтения-записи и двумя буферами, подобными этому:
Writer (case 1):
acquire lock 0
loop
write to current buffer
acquire other lock
free this lock
swap buffers
wait for next period
Or
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
, если бы только писатель мог выполнить обмен после того, как читатель закончит работу, а не ждать следующего периода. В данном случае произошло то, что только из-за того, что один считыватель немного опоздал, все читатели пропустили один кадр данных, а проблемы можно было бы полностью избежать.
Случай 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, который, насколько я понял, продолжает выделять память для буферов и освобождает их до тех пор, пока читатели не закончат с ними работу, что для меня невозможно по многим причинам. Во-первых, потоки распределяются между ядром и пользовательским пространством. Во-вторых, с RTAI вы не можете выделять память в потоке реального времени (потому что тогда ваш поток будет вызывать системные вызовы Linux и, следовательно, нарушать работу в реальном времени! (Не говоря уже о том, что использование собственной реализации RCU в Linux бесполезно из-за по тем же причинам)
Я также подумал о дополнительном потоке, который на более высокой частоте пытается поменять местами буферы, но это не кажется хорошей идеей. Во-первых, ему самому нужно будет синхронизироваться с писателем, а во-вторых, многие из этих писателей-читателей работают в разных частях параллельно, и один дополнительный поток для каждого писателя кажется слишком большим. Один поток для всех писателей кажется очень сложным в плане синхронизации с каждым писателем.