故事
有一个作家线程,定期从某个地方收集数据(实时,但这在问题中并不重要)。然后有很多读者从这些数据中阅读。通常的解决方案是使用两个读写器锁和两个缓冲区,如下所示:
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 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 的系统调用,从而破坏实时性!(更不用说使用 Linux 自己的 RCU 实现了,因为出于同样的原因)
我还考虑过有一个额外的线程以更高的频率尝试交换缓冲区,但这听起来不是一个好主意。首先,它本身需要与 writer 同步,其次,我有许多这样的 writer-readers 并行工作在不同的部分,每个 writer 一个额外的线程似乎太多了。在与每个作者同步方面,所有作者的一个线程似乎非常复杂。