重处理:阶段还是循环线程?

时间:2016-02-22 16:35:58

标签: c# multithreading loops pipeline

我需要创建一个处理大量图像的程序。该过程中大约有10个不同阶段需要按顺序发生。

我想问一下,创建一个管道是否更好,其中每个处理阶段都有自己的线程,并使用此处描述的管道模式在其间进行缓冲: https://msdn.microsoft.com/en-us/library/ff963548.aspx

或创建一个线程池,并使用Parallel.Foreach将一个图像分配给一个线程。

为什么?

2 个答案:

答案 0 :(得分:0)

也许这对你来说是新的东西,但是你可以使用actor系统getakka.net,其中所有作业都由actor完成,因此你可以为每个阶段创建actor并将图像从一个actor传递给另一个actor。 我使用这个框架,因为非阻塞方式的并行处理有了很大的改进。它也可以轻松扩展。

答案 1 :(得分:0)

老实说,没有实际基准测试就没有办法告诉它。但是,您实际上可以使用TPL Dataflow同时并行和管道。

管道中的每个阶段都是TransformBlock<TInput, TOutput>,然后可以并行处理的阶段可以设置Degree of Parallelism

这是一个示例(用浏览器编写,因此可能有错误),它加载带有3级管道的图像,用于从磁盘读取,裁剪图像,然后将其写回磁盘。读写阶段一次只能处理1个图像,但裁剪阶段将同时处理5个图像。此外,管道只允许100个图像排队写入,还有100个图像排队等待裁剪。如果管道已满,它将停止读取图像,并等待管道中有空间(防止过度使用RAM)。

public async Task CropImages(string directory, int x, int y)
{
    var loadImage = new TransformBlock<String, MyImage>(LoadImageAsync);
    var cropImage = new TransformBlock<MyImage, MyImage>((image) => Crop(image, x, y),
                                                         new ExecutionDataflowBlockOptions {MaxDegreeOfParallelism = 5});
    var saveImage = new ActionBlock(SaveImageAsync);


   loadImage.LinkTo(cropImage, new DataflowLinkOptions {PropagateCompletion = true, MaxMessages = 100});
   cropImage.LinkTo(saveImage, new DataflowLinkOptions {PropagateCompletion = true, MaxMessages = 100});

   foreach(var file in Directory.EnumerateFiles(directory, "*.jpg"))
   {
       await loadImage.SendAsync(file);
   }
   loadImage.Complete();
   await saveImage.Completion;
}

private async Task<MyImage> LoadImageAsync(string fileName)
{
    byte[] data = await GetDataAsync(fileName);
    return new MyImage(data, fileName);
}

private MyImage Crop(MyImage image, int x, int y)
{
    image.Crop(x,y);
    return image;
}

private async Task SaveImageAsync(MyImage image)
{
    var fileName = Path.GetFileName(image.FileName);
    var directoryName = Path.GetDirectoryName(image.FileName);
    var newName = Path.Combine(directoryName, "Cropped-" + filename);
    await SaveDataAsync(image.Bytes, newName);
}