Dataflow TPL实现具有前提条件的管道

时间:2016-07-15 16:21:05

标签: task-parallel-library pipeline dataflow tpl-dataflow

我有一个关于使用Dataflow TPL库实现管道的问题。

我的情况是我有一个需要同时处理某些任务的软件。 处理看起来像这样:首先我们在全球范围处理相册,然后我们进入相册并分别处理每张图片。让我们说应用程序已经有了处理槽并且它们是可配置的(为了举例,假设slot = 2)。这意味着应用程序可以处理:

a)两张专辑同时出现 b)一张专辑+一张来自不同专辑的照片 c)同一张专辑同时拍摄两张照片 d)不同专辑同时拍摄两张照片

目前,我实施了以下流程:

TransformBlock<Album, Album> albumTransferBlock = new TransformBlock<Album, Album>(ProcessAlbum, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 2 });

ActionBlock<Album> photoActionBlock = new ActionBlock<Album>(ProcessPhoto);

albumTransferBlock.LinkTo(photoActionBlock);


Album ProcessAlbum(Album a) {
   return a;
}

void ProcessPhoto(Album album) {
 foreach(var photo in album) {
  // do some processing
 }
}

我遇到的问题是,当我处理1张专辑时,应用程序绝不会使用两个插槽来处理照片。它符合除c)之外的所有要求。

有人可以帮我解决使用DataFlow TPL的问题吗?

谢谢! :)

2 个答案:

答案 0 :(得分:0)

我想我可以自己回答。我做的是:

1)我用方法Process()创建了一个接口IProcessor 2)使用接口IProcessor包装AlbumProcessing和PhotoProcessing 3)创建了一个ActionBlock,它将IProcessor作为Input并执行Process方法。

4)在处理专辑结束时,我将所有照片的处理添加到ActionBlock。

这满足了我100%的要求。也许有人有其他解决方案?

答案 1 :(得分:0)

您可以使用TransformManyBlock来处理相册,并链接到ActionBlock来处理照片,以便在处理照片之前先对其进行处理。要施加超出单个块边界的并发限制,可以使用有限并发TaskSchedulerSemaphoreSlim。第二个选项更加灵活,因为它还可以限制异步操作​​。在您的情况下,所有操作都是同步的,因此您可以自由选择任何一种方法。在这两种情况下,您仍应将块的MaxDegreeOfParallelism选项配置为所需的最大并发限制,否则-如果将它们设为unbounded-处理顺序将变得太随机。

这里是TaskScheduler方法的示例。它使用ConcurrentExclusiveSchedulerPair类的ConcurrentScheduler属性:

var options = new ExecutionDataflowBlockOptions
{
    MaxDegreeOfParallelism = 2,
    TaskScheduler = new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default,
        maxConcurrencyLevel: 2).ConcurrentScheduler
};

var albumsBlock = new TransformManyBlock<Album, Photo>(album =>
{
    ProcessAlbum(album);
    return album.Photos;
}, options);

var photosBlock = new ActionBlock<Photo>(photo =>
{
    ProcessPhoto(photo);
}, options);

albumsBlock.LinkTo(photosBlock);

这是SemaphoreSlim方法的示例。使用WaitAsync方法而不是Wait的优点在于,等待获取信号量的过程将asynchronously发生,因此不会ThreadPool线程被不必要地阻塞:< / p>

var throttler = new SemaphoreSlim(2);

var albumsBlock = new TransformManyBlock<Album, Photo>(async album =>
{
    await throttler.WaitAsync();
    try
    {
        ProcessAlbum(album);
        return album.Photos;
    }
    finally { throttler.Release(); }
}, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 2 });

var photosBlock = new ActionBlock<Photo>(async photo =>
{
    await throttler.WaitAsync();
    try
    {
        ProcessPhoto(photo);
    }
    finally { throttler.Release(); }
}, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 2 });

albumsBlock.LinkTo(photosBlock);