C# 第三方物流数据流-非常快的生产者,没有那么快的消费者出内存例外
在将TPL数据流移植到生产代码之前,我正在试验它。 生产代码是一个经典的生产者/消费者系统——生产者产生消息(与金融领域相关),消费者处理这些消息 我感兴趣的是,如果在某一点上生产商的生产速度远远快于消费者的处理速度(系统是否会崩溃,或者会发生什么),环境会保持多稳定&更重要的是,在这种情况下该怎么办 因此,为了尝试使用类似的简单应用程序,我提出了以下建议C# 第三方物流数据流-非常快的生产者,没有那么快的消费者出内存例外,c#,.net,producer-consumer,tpl-dataflow,C#,.net,Producer Consumer,Tpl Dataflow,在将TPL数据流移植到生产代码之前,我正在试验它。 生产代码是一个经典的生产者/消费者系统——生产者产生消息(与金融领域相关),消费者处理这些消息 我感兴趣的是,如果在某一点上生产商的生产速度远远快于消费者的处理速度(系统是否会崩溃,或者会发生什么),环境会保持多稳定&更重要的是,在这种情况下该怎么办 因此,为了尝试使用类似的简单应用程序,我提出了以下建议 var bufferBlock = new BufferBlock<Item>(); var executiondataflo
var bufferBlock = new BufferBlock<Item>();
var executiondataflowBlockOptions = new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = Environment.ProcessorCount
,
BoundedCapacity = 100000
};
var dataFlowLinkOptions = new DataflowLinkOptions
{
PropagateCompletion = true
};
var actionBlock1 = new ActionBlock<Item>(t => ProcessItem(t),
executiondataflowBlockOptions);
bufferBlock.LinkTo(actionBlock1, dataFlowLinkOptions);
for (int i = 0; i < int.MaxValue; i++)
{
bufferBlock.SendAsync(GenerateItem());
}
bufferBlock.Complete();
Console.ReadLine();
GenerateItem
只需发布新闻Item
static Item GenerateItem()
{
return new Item(Guid.NewGuid().ToString());
}
现在,为了模仿速度不太快的消费者,我让ProcessItem
保持100ms
static async Task ProcessItem(Item item)
{
await Task.Delay(TimeSpan.FromMilliseconds(100));
Console.WriteLine($"Processing #{item.ItemId} item.");
}
执行此操作将在20秒左右的时间内导致OOM异常
然后我继续添加了更多的消费者(最多10个ActionBlock),这赢得了更多的时间,但最终导致了相同的OOM异常
我还注意到GC承受着巨大的压力(VS 2015诊断工具显示GC几乎一直在运行),所以我为项引入了对象池(非常简单的一个,本质上是ConcurrentBag
存储项),但我仍然遇到了同样的问题(抛出了OOM异常)
来详细说明内存中的内容以及内存不足的原因
- 最大大小的对象的类型为
SingleProducerSingleConsumerQueue+段
&ConcurrentQueue+段
- 我看到
BufferBlock
的InputBuffer中充满了项
s(Count=14562296)
- 由于我为
ActionBlock
(s)设置了BoundedCapacity
,它们的输入缓冲区也接近配置的数字(InputCount=99996)
为了确保较慢的生产者能够让消费者跟上,我让生产者在迭代之间睡觉:
for (int i = 0; i < int.MaxValue; i++)
{
Thread.Sleep(TimeSpan.FromMilliseconds(50));
bufferBlock.SendAsync(GenerateItem());
}
for(int i=0;i
它工作得很好——没有抛出异常,内存使用率一直很低,我再也看不到任何GC压力
所以我有几个问题
当我试图用TPL数据流构建块重现非常快的生产者/缓慢的消费者场景时,我是否做了一些固有的错误
有没有办法使这项工作,而不是失败的例外
关于如何在TPL数据流上下文中处理此类场景(非常快的生产者/缓慢的消费者)的最佳实践的任何评论/链接
我对这个问题的理解是——由于消费者无法跟上,BufferBlock
的内部缓冲区很快就充满了消息,并延迟消息,直到一些消费者回来请求下一条消息,结果应用程序内存不足(由于BufferBlock
的内部缓冲区已满)-您同意吗
我使用的是Microsoft.Tpl.Dataflow
package-version4.5.24。
.NET4.5(C#6)。进程是32位的。您已经很好地识别了问题:缓冲块正在填充其输入缓冲区,直到到达OOM
要解决此问题,您还应在缓冲区块中添加一个BoundedCapacity
选项。这将为您自动限制生产者(不需要生产者中的线程。Sleep
。以下代码可能存在严重问题:
for (int i = 0; i < int.MaxValue; i++)
{
bufferBlock.SendAsync(GenerateItem()); // Don't do this!
}
这样,生产者将被异步阻止,直到区块内有足够的空间容纳发送的项目。这正是您希望的。否则,如果您不想阻止生产者,请不要使用asynchronousSendAsync
方法,而是使用synchronous方法:
for (int i = 0; i < int.MaxValue; i++)
{
var item = GenerateItem();
while (true)
{
bool accepted = bufferBlock.Post(item); // Synchronous call
if (accepted) break; // Break the inner loop
if (bufferBlock.Completion.IsCompleted) return; // Break both loops
// Here do other things for a while, before retrying to post the item
}
}
幻数1L
是TPL数据流中内部声明的值,表示:
用于只发送一条消息或确切消息ID不重要的代码的已知消息ID
感谢您的确认!请您对BufferBlock的BoundedCapacity
行为发表意见。特别是当内部缓冲区已满时,传入消息会发生什么情况?它们会被丢弃吗?(目前似乎是这样)。您的书中可能有更详细的内容吗?斯蒂芬?@迈克尔:消息永远不会被删除。如果将消息发布到完整块,它将返回false
(表示消息未被接受)。如果等待SendAsync
将消息发送到完整块,它将(异步)返回等待有空位,然后发布消息。看。谢谢@StephenCleary!
for (int i = 0; i < int.MaxValue; i++)
{
await bufferBlock.SendAsync(GenerateItem()); // It's OK now
}
for (int i = 0; i < int.MaxValue; i++)
{
var item = GenerateItem();
while (true)
{
bool accepted = bufferBlock.Post(item); // Synchronous call
if (accepted) break; // Break the inner loop
if (bufferBlock.Completion.IsCompleted) return; // Break both loops
// Here do other things for a while, before retrying to post the item
}
}
for (int i = 0; i < int.MaxValue; i++)
{
var item = GenerateItem();
while (true)
{
var offerResult = ((ITargetBlock<Item>)bufferBlock).OfferMessage(
new DataflowMessageHeader(1L), item, null, false);
if (offerResult == DataflowMessageStatus.Accepted) break;
if (offerResult == DataflowMessageStatus.DecliningPermanently) return;
// Here do other things for a while, before retrying to offer the item
}
}