[英]Producer/Consumer pattern with a batched producer
我正在尝试使用多个生产者和一个消费者来实现一个相当简单的生产者/消费者风格的应用程序。
研究让我进入了BlockingCollection<T>
这很有用,并允许我实现一个长期运行的消费者任务,如下所示:
var c1 = Task.Factory.StartNew(() =>
{
var buffer = new List<int>(BATCH_BUFFER_SIZE);
foreach (var value in blockingCollection.GetConsumingEnumerable())
{
buffer.Add(value);
if (buffer.Count == BATCH_BUFFER_SIZE)
{
ProcessItems(buffer);
buffer.Clear();
}
}
});
ProcessItems
函数将缓冲区提交给数据库,并且它可以批量工作。 然而,该解决方案是次优的。 在男爵生产期间,可能需要一段时间才能填充缓冲区,这意味着数据库已过期。
一个更理想的解决方案是在30秒计时器上运行任务,或者在超时时短路foreach
。
我跑了计时器的想法,想出了这个:
syncTimer = new Timer(new TimerCallback(TimerElapsed), blockingCollection, 5000, 5000);
private static void TimerElapsed(object state)
{
var buffer = new List<int>();
var collection = ((BlockingCollection<int>)state).GetConsumingEnumerable();
foreach (var value in collection)
{
buffer.Add(value);
}
ProcessItems(buffer);
buffer.Clear();
}
这有一个明显的问题,即foreach
将被阻止直到结束,从而打败了计时器的目的。
任何人都可以提供指导吗? 我基本上需要定期快照BlockingCollection
并处理内容清除它。 也许BlockingCollection
是错误的类型?
而不是在计时器回调中使用GetConsumingEnumerable
,而是使用其中一种方法,将结果添加到列表中,直到它返回false
或者您已达到满意的批量大小。
BlockingCollection.TryTake方法(T) - 可能你需要的,你根本不想进一步等待。
BlockingCollection.TryTake方法(T,Int32)
BlockingCollection.TryTake方法(T,TimeSpan)
您可以轻松地将其提取到扩展中(未经测试):
public static IList<T> Flush<T>
(this BlockingCollection<T> collection, int maxSize = int.MaxValue)
{
// Argument checking.
T next;
var result = new List<T>();
while(result.Count < maxSize && collection.TryTake(out next))
{
result.Add(next);
}
return result;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.