我有一个关于使用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的问题吗?
谢谢! :)
答案 0 :(得分:0)
我想我可以自己回答。我做的是:
1)我用方法Process()创建了一个接口IProcessor 2)使用接口IProcessor包装AlbumProcessing和PhotoProcessing 3)创建了一个ActionBlock,它将IProcessor作为Input并执行Process方法。
4)在处理专辑结束时,我将所有照片的处理添加到ActionBlock。
这满足了我100%的要求。也许有人有其他解决方案?
答案 1 :(得分:0)
您可以使用TransformManyBlock
来处理相册,并链接到ActionBlock
来处理照片,以便在处理照片之前先对其进行处理。要施加超出单个块边界的并发限制,可以使用有限并发TaskScheduler
或SemaphoreSlim
。第二个选项更加灵活,因为它还可以限制异步操作。在您的情况下,所有操作都是同步的,因此您可以自由选择任何一种方法。在这两种情况下,您仍应将块的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);