处理大量传入字节数组流的最佳缓冲体系结构

时间:2012-03-03 11:10:33

标签: c# architecture data-structures stream buffer

我正在寻找建议如何最佳架构缓冲结构,该结构可以处理大量传入数据,这些数据的处理速度比传入数据慢。

我编写了一个定制的二进制阅读器,它可以在一个线程上每秒流式传输多达1200万字节数组,并且可以在同一台机器和不同线程上的单独结构中处理字节数组流。问题是消费结构无法跟上生产者的输入数据量,因此我认为我需要某种缓冲来正确处理这个问题。我最感兴趣的是有关整体架构的建议,而不是代码示例。我的目标是.Net 4.0。以下是我当前设置和要求的更多信息。

生产者:在专用线程上运行并从物理存储介质(SSD,OCZ Vertex 3 Max IOPS)上的文件读取字节数组。近似吞吐量是每秒1200万字节数组。每个数组只有16字节大小。全面实施

Consumer:假设在一个单独的线程上运行而不是producer.Consumes字节数组但必须在处理数据之前解析为几种原始数据类型,因此处理速度明显慢于生产者发布速度。消费者结构已全面实施。

介于两者之间:希望设置一个缓冲结构,为生产者提供发布,消费者可以使用。没有实施。

如果你们中的一些人可以根据自己的经验或专业知识评论为了处理这种结构而最好考虑的事情,我会很高兴。缓冲区应该实现一种限制算法,当缓冲区/队列半空时,它只会从生产者请求新数据吗?如何处理锁定和阻塞?对不起,我在这个领域的经验非常有限,到目前为止通过实现消息传送总线来处理它,但我看到的任何消息传递总线技术肯定无法处理我正在寻找的吞吐量。任何评论都非常欢迎!!!

编辑:忘记提及,数据仅由一个消费者使用。数组发布的顺序也很重要;需要保留订单,以便消费者以相同的顺序消费。

4 个答案:

答案 0 :(得分:1)

你可以使用BlockingCollection只要消费者没有消耗足够的物品,它就会阻止生产者向集合中添加物品。

还有其他并发集合类,例如。 ConcurrentQueue

答案 1 :(得分:1)

16个字节(称之为16B)对于高效的线程间通信来说太小了。对这些小缓冲区进行排队将导致在线程间通信上花费的CPU比在实际有用的数据处理上花费更多。

所以,把它们搞定。

声明一些缓冲类,(C16B,比如说),它包含一个漂亮的,大的16B的数组 - 至少4K的价值,以及一个'count'int来显示加载的数量,(最后一个缓冲区来自一个文件可能不会满了。如果你在这个16B阵列的前面放置一个缓存行大小的空字节数组会有所帮助 - 有助于避免错误共享,你可以将处理16B的代码作为一种方法,'Process16B',sya ,也许是加载数组的代码 - 将文件描述符作为参数。现在可以有效地将此类加载到其他线程的队列中。

您需要一个生产者 - 消费者队列类--C#已经在BlockingCollection类中有一个。

您需要在此应用中使用流控制。我会通过创建一个C16B池来创建 - 创建一个阻塞队列并在循环中创建/添加一大堆C16B。 1024是一个不错的圆形数字。现在你有一个提供流量控制的“池队列”,避免了对new()任何C16B的需要,你不需要它们不断被垃圾收集。

一旦你有了这个,其余的很容易。在您的加载器线程中,不断地将C16B从池队列中取出,使用来自文件的数据加载它们,并将它们添加到“16Bprocess”阻塞队列上的处理线程中。在处理线程中,从16Bprocess队列中取出()并通过调用其Process16B方法处理每个C16B实例。处理16B时,将C16B添加()回池名队列以便重复使用。

通过池队列回收C16B可提供端到端的流量控制。如果生产者是最快的链接,则池最终将为空,生产者将阻止那里,直到消费者返回一些C16B。

如果处理需要花费很多时间,那么如果有可用的备用内核,则可以随时添加另一个处理线程。这种方案的障碍是数据将被无序处理。这可能,也可能不重要。如果是这样,数据流可能需要稍后“理顺”,例如。使用序列号和缓冲列表。

我建议将池队列计数(也可能是16Bprocess队列计数)转储到带有计时器的状态组件或命令行。这提供了C16B实例所在位置的有用快照,您可以在没有第三方工具的情况下看到瓶颈和任何C16B泄漏(将整个应用程序降级为爬行并在关闭时发出虚假泄漏报告的那些)。 / p>

答案 2 :(得分:0)

IMO阻止某种队列可能会解决您的问题。如果队列没有更多容量,生成器线程基本上将阻塞。看看这个Creating a blocking Queue<T> in .NET?

答案 3 :(得分:0)

为什么要使用缓冲区呢?将磁盘文件用作缓冲区。当消费者开始处理字节数组时,让读者阅读下一个字节数组。

编辑:在要求消费者和制片人脱钩之后。

你可以有一个协调器,告诉生产者生成X字节数组,并向消费者提供X字节数组。这三个部分可以这样做:

协调器告诉生产者生成X字节数组。 生产者生成X字节数组

现在循环执行此操作: 协调器告诉消费者消费者X字节数组 协调器告诉生产者生成X字节数组 消费者告诉协调员它已经消耗完了 循环,直到没有更多的字节数组

生产者和协调者可以在同一个线程中运行。消费者应该有自己的主题。

你几乎没有锁定(我认为你可以完全没有锁定,只需要消费者用来通知协调员的一个等待句柄),你的协调员非常简单。

REEDIT:另一个真的解耦选项

使用ZeroMQ来处理通信。生产者读取字节数组并将每个数组发布到ZeroMQ套接字。消费者从ZeroMQ套接字读取数组。

ZeroMQ非常高效和快速,并在内部处理所有技术问题(线程同步,缓冲等)。在同一台计算机上使用时,您也不会遭受任何数据丢失(在两台不同的计算机上使用UDP时可能会发生这种情况)。