TPL Dataflow:在保持秩序的同时设计并行性

时间:2014-02-03 00:35:35

标签: c# asynchronous task-parallel-library dataflow tpl-dataflow

之前我从未使用过TPL,所以我想知道是否可以用它完成: 我的应用程序从很多帧创建一个gif图像动画文件。我从一个Bitmap列表开始,它代表了gif文件的帧,需要为每个帧执行以下操作:

  1. 在框架上绘制一些文本/位图
  2. 裁剪框架
  3. 调整框架大小
  4. 将图像缩小为256色
  5. 显然,这个过程可以对列表中的所有帧并行完成,但对于每个帧,步骤的顺序必须相同。 之后,我需要将所有帧写入gif文件。因此,需要以与原始列表中相同的顺序接收所有帧。最重要的是,此过程可以在第一帧准备就绪时开始,无需等到所有帧都被处理完毕。

    这就是情况。 TPL Dataflow适合这个吗?如果是的话,任何人都可以给我一个关于如何设计tpl块结构以反映上述过程的正确方向的提示吗?与我发现的一些样品相比,这对我来说似乎相当复杂。

3 个答案:

答案 0 :(得分:4)

我认为使用TPL Dataflow是有道理的,特别是因为即使启用了并行性,它也会自动保持处理元素的正确顺序。

您可以为流程中的每个步骤创建一个单独的块,但我认为这里没有必要,一个用于处理帧的块和一个用于编写它们的块就足够了:

public Task CreateAnimationFileAsync(IEnumerable<Bitmap> frames)
{
    var frameProcessor = new TransformBlock<Bitmap, Bitmap>(
        frame => ProcessFrame(frame),
        new ExecutionDataflowBlockOptions
        { MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded });

    var animationWriter = new ActionBlock<Bitmap>(frame => WriteFrame(frame));

    frameProcessor.LinkTo(
        animationWriter,
        new DataflowLinkOptions { PropagateCompletion = true });

    foreach (var frame in frames)
    {
        frameProcessor.Post(frame);
    }

    frameProcessor.Complete();

    return animationWriter.Completion;
}

private Bitmap ProcessFrame(Bitmap frame)
{
    …
}

private async Task WriteFrame(Bitmap frame)
{
    …
}

答案 1 :(得分:2)

我认为你会发现DataFlow是正确的方法。对于每个帧,从您的帧列表中,尝试创建一个TransformBlock。对于四个步骤中的每个步骤,以正确的顺序将帧链接在一起。如果要同时处理框架列表,可以使用bufferblock作为框架列表。

please find the full sample on how to use transformblock on msdn:

答案 2 :(得分:2)

您的问题是数据流优势所在的完美示例。

这是最简单的代码,可以帮助您入门。

// Try increasing MaxDegreeOfParallelism
var opt = new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 2 };

// Create the blocks
// You must define the functions to do what you want
var paintBlock = new TransformBlock<Bitmap, Bitmap>(fnPaintText, opt);
var cropBlock = new TransformBlock<Bitmap, Bitmap>(fnCrop, opt);
var resizeBlock = new TransformBlock<Bitmap, Bitmap>(fnResize, opt);
var reduceBlock = new TransformBlock<Bitmap, Bitmap>(fnReduce,opt);

// Link the blocks together
paintBlock.LinkTo(cropBlock);
cropBlock.LinkTo(resizeBlock);
resizeBlock.LinkTo(reduceBlock);

// Send data to the first block
// ListOfImages contains your original frames
foreach (var img in ListOfImages) { 
   paintBlock.Post(img);
}

// Receive the modified images
var outputImages = new List<Bitmap>();
for (int i = 0; i < ListOfImages.Count; i++) {
   outputImages.Add(reduceBlock.Receive());
}

// outputImages now holds all of the frames
// reassemble them in order
相关问题