C# 带前置条件的数据流TPL实现流水线

C# 带前置条件的数据流TPL实现流水线,c#,task-parallel-library,pipeline,dataflow,tpl-dataflow,C#,Task Parallel Library,Pipeline,Dataflow,Tpl Dataflow,我有一个关于使用Dataflow TPL库实现管道的问题 我的情况是,我有一个软件,需要同时处理一些任务。 处理过程如下所示:首先,我们在全局级别处理相册,然后进入相册内部,分别处理每张图片。假设应用程序有处理槽,并且它们是可配置的(为了示例起见,假设槽=2)。这意味着应用程序可以处理以下任一项: a) 同时发行两张专辑 b) 一本相册+不同相册中的一张照片 c) 同一相册中同时有两张照片 d) 两张不同相册的照片同时拍摄 目前,我实施了如下流程: var albumTransferBlock

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

我的情况是,我有一个软件,需要同时处理一些任务。 处理过程如下所示:首先,我们在全局级别处理相册,然后进入相册内部,分别处理每张图片。假设应用程序有处理槽,并且它们是可配置的(为了示例起见,假设槽=2)。这意味着应用程序可以处理以下任一项:

a) 同时发行两张专辑
b) 一本相册+不同相册中的一张照片
c) 同一相册中同时有两张照片
d) 两张不同相册的照片同时拍摄

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

var 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
    }
}
var albumTransferBlock=新的TransformBlock(ProcessAlbum,
新的ExecutionDataflowBlockOptions{MaxDegreeOfParallelism=2});
ActionBlock photoActionBlock=新的ActionBlock(ProcessPhoto);
albumTransferBlock.LinkTo(photoActionBlock);
相册处理相册(相册a)
{
返回a;
}
void ProcessPhoto(相册)
{
foreach(相册中的var照片)
{
//做一些处理
}
}
我遇到的问题是,当我同时处理一个相册时,应用程序将永远不会使用两个插槽来处理照片。它满足除c)以外的所有要求


有人能帮我用DataFlow TPL解决这个问题吗?

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

1) 我用方法Process()创建了一个接口IPProcessor 2) 使用接口IProcessor进行图像处理和照片处理 3) 创建了一个ActionBlock,它将IPProcessor作为输入并执行Process方法

4) 在处理相册的末尾,我将所有照片的处理添加到ActionBlock


这100%满足了我的要求。也许有人有别的解决办法?

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

1) 我用方法Process()创建了一个接口IPProcessor 2) 使用接口IProcessor进行图像处理和照片处理 3) 创建了一个ActionBlock,它将IPProcessor作为输入并执行Process方法

4) 在处理相册的末尾,我将所有照片的处理添加到ActionBlock

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

您可以使用一个链接到一个
ActionBlock
的相册来处理照片,这样每个相册在处理照片之前都会被处理。要施加超出单个块边界的并发限制,可以使用受限并发
TaskScheduler
信号量lim
。第二个选项更灵活,因为它还允许限制异步操作。在您的情况下,所有操作都是同步的,因此您可以自由选择其中一种方法。在这两种情况下,您仍然应该将块的
MaxDegreeOfParallelism
选项配置为所需的最大并发限制,否则(如果您设置了它们),处理顺序将变得过于随机

下面是
TaskScheduler
方法的一个示例。它使用类的
ConcurrentScheduler
属性:

您可以使用链接到
ActionBlock
的相册来处理照片,以便在处理每个相册的照片之前对其进行处理。要施加超出单个块边界的并发限制,可以使用受限并发
TaskScheduler
信号量lim
。第二个选项更灵活,因为它还允许限制异步操作。在您的情况下,所有操作都是同步的,因此您可以自由选择其中一种方法。在这两种情况下,您仍然应该将块的
MaxDegreeOfParallelism
选项配置为所需的最大并发限制,否则(如果您设置了它们),处理顺序将变得过于随机

下面是
TaskScheduler
方法的一个示例。它使用类的
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);
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);