使用Producer Consumer实现批处理

时间:2012-10-16 14:49:57

标签: c# .net task-parallel-library producer-consumer

批量

  1. 从文件或SQL中读取文本
  2. 将文本解析为单词
  3. 将单词加载到SQL
  4. 今天
    .NET 4.0
    第1步非常快。 对于相同尺寸的文件,步骤2和3的长度大约相同(平均0.1秒) 在步骤3中插入使用BackGroundWorker并等待最后完成 其他一切都在主线上。

    在很大的负荷上会做几百万次。

    需要步骤3为序列号,顺序为1 这是为了防止SQL表PK索引破裂 并行尝试了第3步,压缩指数使其死亡 这些数据按PK排序 其他索引在加载开始时被删除,然后在加载结束时重建。

    此过程无效的地方是文本大小发生变化时 从文件到文件的文本大小确实发生了巨大变化 我想要的是队列1和2所以3尽可能地保持忙碌。

    需要步骤3将文件排队,以便它们在1中排队(即使它等待)。

    内存管理需要最大队列大小(如4-10)。

    希望步骤2与最多4个并发进行并行。

    迁移到.NET 4.5。

    询问有关如何实施此方法的一般指导?

    我知道这是一个生产者消费模式 如果这不是生产者消费者模式,请告诉我,以便我可以更改标题。

1 个答案:

答案 0 :(得分:2)

我认为TPL Dataflow是一个很好的方法:

对于第2步,您可以使用TransformBlock MaxDegreeOfParallelism设置为4而BoundedCapacity也设置为4,以便在工作时其队列为空。它会以与它们相同的顺序生成项目,您不必为此做任何特殊操作。对于第3步,请使用ActionBlock,并将BoundedCapacity设置为限制。然后将两者链接在一起并开始向TransformBlock发送项目,理想情况下使用类似await stepTwoBlock.SendAsync(…)的内容,以便在队列已满时异步等待。

在代码中,它看起来像:

async Task ProcessData()
{
    var stepTwoBlock = new TransformBlock<OriginalText, ParsedText>(
        text => Parse(text),
        new ExecutionDataflowBlockOptions
        {
            MaxDegreeOfParallelism = 4,
            BoundedCapacity = 4
        });
    var stepThreeBlock = new ActionBlock<ParsedText>(
        text => LoadIntoDatabase(text),
        new ExecutionDataflowBlockOptions { BoundedCapacity = 10 });
    stepTwoBlock.LinkTo(
        stepThreeBlock, new DataflowLinkOptions { PropagateCompletion = true });

    // this is step one:
    foreach (var id in IdsToProcess)
    {
        OriginalText text = ReadText(id);
        await stepTwoBlock.SendAsync(text);
    }

    stepTwoBlock.Complete();
    await stepThreeBlock.Completion;
}