如何在C++中安全地覆盖完整circular_buffer中的数据

问题描述 投票:0回答:1

我目前正在尝试思考如何安全地处理循环缓冲区已满并且生产者想要将数据插入其中的情况。 原因是我遇到了一个典型的生产者-消费者问题,相机(生产者)生成图像,查看器(消费者)显示图像,两者都在自己的线程中运行。由于我不想限制相机的帧速率(也许这不是最好的主意,但我们假设它是),生产者可能会在某些时候覆盖消费者尚未处理的图像。 由于图像可能相当大,我会选择不按值返回它们,而是按引用或指针返回它们。因此,一旦缓冲区已满,生产者可能会覆盖消费者当前持有的指针的图像,甚至可能在消费者读取其内存位置时也是如此。当然,这是一种需要避免的行为,因为消费者不希望数据在他的手指下改变。

但是,我想知道人们真正期望的行为是什么,以及哪种解决方案是最好的、最封装的解决方案。

由于不希望限制相机帧速率,因此一旦尝试覆盖活动图像,就不能简单地使用互斥体或信号量来暂停生成器。因此,我在这里能想到的唯一解决方案是跳过消费者当前处理的图像并覆盖第二旧的图像。然而,为此需要一种机制来指示当前正在使用该图像。也许在请求图像时返回

std::shared_ptr
并在覆盖图像时检查其
use_count
是一种选择。

我试图找到类似的问题,但找不到。任何如何正确解决这个问题的输入,或者如果这个问题不是问题,因为我的基本想法有缺陷,都会有所帮助。非常感谢!

c++ circular-buffer
1个回答
0
投票

我认为放弃真正的循环缓冲区的想法是最容易的。循环缓冲区的主要动机是它们是表示小记录的大队列的最有效方式,因为没有每条记录的内存开销。这是由非常小的元素组成的有用队列(例如,具有 16 或 32 位样本的音频缓冲区),但正如您所发现的,它不能很好地支持您的用例。

在您的示例中,队列条目(包含视频帧的缓冲区)可能非常大(如果不是兆字节,也是千字节?),因此您不妨将它们分配在堆上,并将指向缓冲区的指针放入简单的类似队列的数据中结构。

对于您的问题,只要有:

  • 预分配缓冲区的空闲列表。
  • 要消耗的已填充缓冲区列表。

然后消费者会做类似的事情:

  1. 从列表 2 的前面抓取一个缓冲区(如果为空则阻塞)。
  2. 处理它。
  3. 将缓冲区返回到列表1以供重用。

制作人:

  1. 从列表 1 中抓取一个缓冲区,或者如果它是空的,则从列表 2 的前面抓取它(这就是最终覆盖最旧的已填充缓冲区的方式)。
  2. 填充缓冲区。
  3. 将缓冲区添加到要处理的列表2的后面。

列表可以是简单的`std::deque。这里不需要智能指针,因为任何时候都不会共享任何缓冲区。缓冲区要么在空闲列表中,要么在队列中,由生产者拥有,或者由消费者拥有,中间没有任何东西。

然而,重要的是对双端队列(或您决定使用的任何数据结构)的访问是同步的。一个简单的互斥锁可能就足够了(与信号量结合来处理消费者需要在步骤 1 中阻塞和等待的情况)。您还可以使用队列的无锁实现,但性能差异不太可能大到足以对此用例产生影响。

请注意,如果需要,您仍然可以在连续内存中分配所有缓冲区。重要的是,您只将指针存储在队列中,以便同步访问可以非常快。

© www.soinside.com 2019 - 2024. All rights reserved.