【问题标题】:C++: Queue with efficient get/put of multiple elements?C ++:具有多个元素的有效获取/放置的队列?
【发布时间】:2011-07-24 20:00:01
【问题描述】:

所以,我觉得在 C++ 中应该有一个很好的内置解决方案,但我不确定它是什么。

我需要一个有效处理字节组的队列(理想情况下是线程安全的,但如果需要,我可以自己同步包装它) - 允许不同大小的读/写。

所以,界面看起来像例如

//removes the first bytesToRead elements from the front of the queue and places them in array; returns the actual number of bytes dequeued
int dequeue(unsigned char *array, int bytesToRead) 
//Adds bytesToWrite elements from array to the end of the queue; does nothing and returns 0 if this would exceed the queue's max size
int enqueue(unsigned char *array, int bytesToWrite)

我可以自己写一个没有太多困难,但似乎这应该是现成的很容易完成的事情。

STL 中最好的东西看起来可能是一个 stringbuf - 我必须手动配对对 sgetc/pubseekoff 的调用,但它似乎可以工作。

我希望这样做是为了替代当前存在性能问题的队列实现;在这个实现中读取队列中的数据量是 O(N)。 (这是一个非常幼稚的实现——每次出队都会导致队列中剩余数据的数组副本。)

其他要求(如果需要,我可以在包装器中实现这些): -我需要能够指定缓冲区的最大大小 - 如果可用数据少于请求的数据,则读取操作应检索所有可用数据 - 如果请求的写入将超过最大大小并返回失败指示符,则写入操作不应执行任何操作

所以,我的问题: 1) stringbuf 是否足够?假设不需要调整大小,读/写操作是否相对于缓冲区中的数据量 O(1)? (显然,在请求的项目数量上,它们可能是 O(n)。)

2) 是否还有其他我看不到的课程就足够了?

提前致谢!

【问题讨论】:

  • 我怀疑,除非您只是通过实现为链表的队列传递指向缓冲区的指针,否则您从不必将数据复制到出列队列中获得的任何好处都将因额外的开销而丢失入队时复制或分配。
  • @Jon:听起来好像问题不是被复制的出队元素,而是整个队列的其余部分向下移动。有很多数据结构比这做得更好。
  • 啊,好吧,我没有从 OPs 描述中得到那个。

标签: c++ stl queue


【解决方案1】:

嗯...你有没有尝试过显而易见的:

class queue { 
      std::deque<unsigned char> data;
public:
    int enqueue(unsigned char *array, int bytesToWrite) { 
        data.insert(data.end(), array, array+bytesToWrite);
    }

    int dequeue(unsigned char *array, int bytesToRead) { 
        std::copy(data.begin(), data.begin()+bytesToRead, array);
        // or, for C++11: std::copy_n(data.begin(), bytesToRead, array);

        data.erase(data.begin(), data.begin()+bytesToRead);
    }
};

抱歉,我目前还没有足够的野心来添加锁定和您要求的返回值,但两者都不应该是非常困难的。但是,我不会摆弄您的返回值,而是将接口更改为使用迭代器(或者,如果您真的坚持,使用对向量的引用)。

这保证与插入/删除的元素数量成线性关系。

【讨论】:

  • 应该是array + bytesToWrite
  • 在您的 dequeue 函数中,您使用指针算术,在我看来,标准明确规定 deque 不允许“通过指针算术安全访问”。还是我弄错了。 cplusplus.com/reference/stl/deque
  • @Mikael Persson:这不是指针算术——它是迭代器算术。 std::deque 提供随机访问,这意味着支持迭代器算术,即使/当指针算术不起作用时也是如此。
【解决方案2】:

如果您想要一个真正快速高效的实现,我会选择一个简单的循环缓冲区实现,这意味着您可以读取一个或两个副本(取决于您是否在缓冲区的结尾/开头进行包装) .这允许您使用 memcpy,根据我的经验,它几乎总是优于循环遍历大量元素来进行复制。

如果性能不那么重要,尽管我会选择 Jerry 的答案。

【讨论】:

    【解决方案3】:

    您能否使用std::stringstreamwrite 推入队列并使用read 退出队列?

    【讨论】:

      【解决方案4】:

      正如建议的那样,std::stringstream 可能是最简单和最好的解决方案。

      另一种选择是std::deque,它将为您提供所需的效率(从队列两端的所有读/写的常数摊销,如果容量耗尽,通常远小于重新分配的 O(N)) .唯一的缺点是 std::deque 不支持指针运算(因为所有元素不一定是连续的(以块的形式)),因此您将无法进行块读/写操作,您将不得不迭代,因为如下:

      std::deque<unsigned char> buf;
      
      int dequeue(unsigned char *array, int bytesToRead) {
        int result = std::min(bytesToRead, buf.size());
        std::copy(buf.begin(), buf.begin() + result, array);
        buf.erase(buf.begin(), buf.begin() + result);
        return result;
      }; 
      
      int enqueue(unsigned char *array, int bytesToWrite) {
        buf.insert(buf.end(), array, array + bytesToWrite);
        return bytesToWrite;
      };
      

      您可能应该让后一种实现检查是否达到最大容量并相应地调整结果值。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-09
        相关资源
        最近更新 更多