【发布时间】:2016-07-10 22:11:03
【问题描述】:
我们有一个一般的日志记录问题,我们执行 3 种类型的日志记录(让我们说:跟踪、审计、计数)是我们执行的 3 个日志记录领域。我们同时执行所有这些操作,从在 REST Web 服务中运行的代码开始,针对传入的每个 Web 请求。
对于每个发出的请求,我们都会对每个日志记录区域进行多次调用。可以说,每个 Web 请求、一些跟踪、一些审计、一些计数器、每个 Web 请求总共有 100 个调用,平均总共有大约 100 个日志记录调用。每个日志调用都会将一些数据写入某个存储以供以后处理[存储是什么并不重要,但是将其放入该存储肯定是 I/O 绑定的——(实际上它是一个 azure 队列,因此它是跨互联网的 HTTP 调用) ]。
我们的问题是,记录任何信息(在 3 个区域中的任何一个中)的行为会使服务请求线程花费太长的时间来写入所有日志接收器 100 次。我们测量到多达 4/5 的请求处理时间用于写入日志接收器!因此,您可以看到我们需要对其进行显着优化!
我们想要加快速度,并让请求线程免于写入日志。我们想使用另一个线程在后台按照自己的节奏将日志数据写入 3 个日志存储。因此,我们认为将日志写入排队到内存队列中,以便日志写入线程可以在多个请求进入时并行处理它们,这是一种可行的方法。
Stephen Cleary 的“Concurrency in C# Cookbook”一书(fab 参考)指出,在这种情况下,使用像 BlockingCollection<T> 这样的阻塞集合是理想的。这样一个线程一次可以产生数据(并写入内存队列),另一个线程可以消耗内存队列中的数据。看起来这对我们的情况来说是理想的。
但是,在我们的例子中,因为我们在 ASP.NET 主机中运行,并且线程池线程对于 Web 服务器来说非常宝贵(为了可扩展性),所以我们只希望有一个线程专用于 [消费] 所有3 个日志记录队列。让所有其他线程处理入站 Web 请求,并 [产生] 日志数据。
那么问题就变成了:如何使用BlockingCollection<T>的3个实例(每种日志类型一个),并支持:
- 用于生产数据的任意数量的生产者 [入站 Web 请求线程]
- 单个(专用)日志记录线程使用者,始终连续高效地消耗所有三个队列。
任何人都可以想到一个在这里可以很好地工作的设计模式吗?对我们来说,缺少的部分是消费者如何有效地清空所有三个队列,而不阻塞其中任何一个,并持续处理所有三个队列。
【问题讨论】:
-
您可以有一个
BlockingCollection<T>,其中T是一些自定义类,可以包含三种类型中的任何一种。您可以在此类型中使用枚举来检测该类真正拥有的类型。 -
谢谢,但我没有从您的回答中看到我们如何使用相同的消费者线程连续处理所有三个
BlockingCollection<T>队列,而无需等待任何一个队列。 -
我建议你有一个
BlockingCollection,而不是三个。 -
我现在明白了。感谢您的澄清。枚举描述了数据,最终帮助消费者决定将其添加到哪个商店。是的,这可以工作。
标签: c# multithreading asynchronous concurrency producer-consumer