4

这是我需要完成的任务列表:

  1. 读取文件的一大块(磁盘 IO 绑定)
  2. 加密所述块(CPU绑定)
  3. 上传所述块(网络 IO 绑定)
  4. 重复直到文件上传

问题在于如何以最大的效率和性能实现这一目标。

我尝试使用Parallel.For来封装整个操作块,但我认为这不是解决此问题的最佳方法,因为每个操作都有不同的特征可以解释(正如我在上面的列表中指出的那样) .

在阅读了这个问题中建议的这篇TPL 文章之后,并且在回顾了那个问题中的经验数据之后,我认为 TPL 是我想要走的路。但是我应该如何分解它以获得最大的效率和性能?考虑到上传可能是整个操作的瓶颈,我是否应该费心尝试对前两个操作进行多线程处理?

感谢您的输入。

编辑:

我尝试使用 Tasks 和 ContinueWith 让操作系统处理它,但我认为我遇到了另一面墙——当我等待所有上传任务完成时,垃圾收集器似乎没有清理我读入要上传的数据,因此最终内存不足。另一个必须考虑。

4

1 回答 1

1

如果您不能使用.Net 4.5,我建议您使用一个线程从磁盘读取,一个线程用于加密,一个线程用于上传。BlockingCollection<byte[]>为了在它们之间进行通信,您可以在每对线程(1-2 和 2-3)之间使用生产者-消费者模式。

但是由于您可以使用 .Net 4.5,因此您可以使用 TPL Dataflow,它非常适合这项任务。使用 TPL 数据流意味着您不会浪费线程来读取和上传(尽管这对您来说很可能并不重要)。更重要的是,这意味着您可以轻松地并行加密每个块(假设您可以这样做)。

你要做的是有一个块用于加密,一个块用于上传和一个异步任务(实际上,它不必是一个完整的Task)用于从文件中读取。可以将用于加密的块配置为并行执行,并且两个块都应配置为具有最大容量(否则,节流将无法正常工作,并且将尽可能快地读取整个文件,这可能导致 OutOfMemoryException)。

在代码中:

var uploadBlock = new ActionBlock<byte[]>(
    data => uploadStream.WriteAsync(data, 0, data.Length),
    new ExecutionDataflowBlockOptions { BoundedCapacity = capacity });

var encryptBlock = new TransformBlock<byte[], byte[]>(
    data => Encrypt(data),
    new ExecutionDataflowBlockOptions
    {
        BoundedCapacity = capacity,
        MaxDegreeOfParallelism = degreeOfParallelism
    });

encryptBlock.LinkTo(
    uploadBlock,
    new DataflowLinkOptions { PropagateCompletion = true });

while (true)
{
    byte[] chunk = new byte[chunkSize];
    int read = await fileStream.ReadAsync(chunk, 0, chunk.Length);
    if (read == 0)
        break;
    await encryptBlock.SendAsync(chunk);
}

fileStream.Close();
encryptBlock.Complete();
await uploadBlock.Completion;
uploadStream.Close();
于 2012-12-22T14:09:18.590 回答