生产者 - 消费者多个生产者多个队列单个消费者

时间:2016-07-10 22:11:04

标签: c# multithreading asynchronous concurrency producer-consumer

我们有一个常规的日志记录问题,我们做3种类型的日志记录(简单地说:跟踪,审计,计数)是我们做的3个日志记录区域。我们同时从REST Web服务中运行的代码中为每个进入的Web请求执行所有操作。

对于每个请求,我们会对每个日志记录区域进行多次调用。可以说,平均每个Web请求总共有大约100个日志记录调用,一些跟踪,一些审计,一些计数器,总共100个调用,每个Web请求。每个日志记录调用都会将一些数据写入某个存储区以便以后处理[不重要的是存储是什么,但是进入该存储区肯定是I / O绑定 - (实际上它是一个天蓝色队列,所以它跨越interweb进行HTTP调用) ]

我们的问题是记录任何信息(在3个区域中的任何一个区域中)的行为使服务请求线程花费太长时间来写入所有日志接收器100次。我们已经测量了最多4/5的请求处理时间用于写入日志记录接收器!所以,你可以看到我们需要对其进行大幅优化!

我们希望加快速度,并从日志编写中释放请求线程。我们希望使用另一个线程在后台以自己的节奏将日志数据写入3个日志记录存储。因此,我们认为将日志记录写入内存队列,以便日志写入线程可以并行处理多个请求,这是一种方法。

Stephen Cleary的书“C#Cookbook中的并发”(工厂参考)确定在这种情况下使用像window.submit()这样的阻塞集合是理想的。这样一次一个线程就可以产生数据(并写入内存队列),而另一个线程可以使用内存队列中的数据。似乎它对我们的情况来说是理想的。

但是,在我们的例子中,因为我们在ASP.NET主机上运行,​​并且线程池线程对于Web服务器非常宝贵(为了可伸缩性),我们只希望有一个专用于[使用]所有的线程3个记录队列。保留所有其他线程来处理入站Web请求,并[生成]日志记录数据。

问题变成了:如何使用BlockingCollection<T>的3个实例(每种类型的日志记录一个)和支持:

  1. 用于生成数据的任意数量的生产者[入站Web请求线程]
  2. 单个(专用)日志记录线程使用者,它会持续有效地消耗所有三个队列。
  3. 有人能想到一个在这里运作良好的设计模式吗?对我们来说,缺失的部分是消费者如何有效地清空所有三个队列,而不阻塞任何队列,并持续处理所有这三个队列。

1 个答案:

答案 0 :(得分:1)

这是我在类似场景中成功使用的模式。

首先,我通常使用ConcurrentQueue<T>因为没有任何需要阻止的场景。也许这会根据您的日志记录量而有所不同。队列包含在Buffer类中。它可能只包含多个队列。

所有请求线程都将项目放入队列中。在同一个线程上,Buffer正在检查数量与最大大小,如果确定需要刷新缓冲区,则在单独的线程上执行此操作。还有一个计时器,即使缓冲区未满,也会刷新缓冲区。在任何一种情况下都要检查以确保缓冲区尚未刷新。

结果是,除非缓冲区永久满,否则不需要总是专用于日志的线程,在没有消息时阻塞。

您还可以通过批量发送消息而不是单独发送消息来提高性能。您可以通过发送一条包含50-100条消息的“消息”来消除大量开销,而不是分别发送每条消息。

相关问题