簡體   English   中英

TPL Dataflow:在保持秩序的同時設計並行性

[英]TPL Dataflow: design for parallelism while keeping order

我之前從未使用過TPL,所以我想知道是否可以用它完成:我的應用程序從很多幀創建一個gif圖像動畫文件。 我從一個Bitmap列表開始,它代表了gif文件的幀,需要為每個幀執行以下操作:

  1. 在框架上繪制一些文本/位圖
  2. 裁剪框架
  3. 調整框架大小
  4. 將圖像縮小為256色

顯然,這個過程可以對列表中的所有幀並行完成,但是對於每個幀,步驟的順序必須相同。 之后,我需要將所有幀寫入gif文件。 因此,需要以與原始列表中相同的順序接收所有幀。 最重要的是,此過程可以在第一幀准備就緒時開始,無需等到所有幀都被處理完畢。

這就是情況。 TPL Dataflow適合這個嗎? 如果是的話,任何人都可以給我一個關於如何設計tpl塊結構以反映上述過程的正確方向的提示嗎? 與我發現的一些樣品相比,這對我來說似乎相當復雜。

我認為使用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)
{
    …
}

我想你會發現DataFlow是正確的方法。 對於每個幀,從幀列表中嘗試創建一個TransformBlock 對於四個步驟中的每個步驟,以正確的順序將幀鏈接在一起。 如果要同時處理幀列表,可以使用bufferblock作為幀列表。

請在msdn上找到有關如何使用transformblock的完整示例:

您的問題是數據流擅長的一個完美示例。

這是最簡單的代碼,可以幫助您入門。

// 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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM