История
Существует поток писателя, периодически собирающий данные откуда-то (в режиме реального времени, но это не имеет большого значения в вопросе).Тогда многие читатели читают по этим данным.Обычное решение для этого заключается в использовании двух блокировок чтения-записи и двух буферов, например:
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
, если бы только писатель мог выполнить обмен после завершения считывателя вместо того, чтобы ждать егоследующий периодВ этом случае произошло то, что только из-за того, что один читатель немного опоздал, все читатели пропустили один кадр данных, а проблемы можно было полностью избежать.
Случай 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 lock0 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 бесполезно из-запо тем же причинам)
Я также думал о том, чтобы иметь дополнительный поток, который на более высокой частоте пытается поменять буферы, но это не очень хорошая идея. Во-первых, он сам должен был бы синхронизироваться списатель, и, во-вторых, у меня есть много этих писателей-читателей, работающих в разных частях параллельно, и один дополнительный поток для каждого писателя кажется слишком большим. Один поток для всех писателей кажется очень сложным в отношении синхронизации с каждым писателем.