我正在开发一个多段文件下载程序。为了完成这项任务,我目前正在磁盘上创建与我所拥有的段一样多的临时文件(在文件下载过程中,它们的数量是固定的)。最后,我只创建了一个新文件f
,并将所有片段的内容复制到f
上。
我想知道是否没有更好的方法来实现这一点。我的理想化是最初创建完全大小的f
,然后让不同的线程直接写入它们的部分。他们之间不需要有任何形式的互动。我们可以假设它们中的任何一个都将从文件中自己的起点开始,然后只在文件中按顺序填充信息,直到任务结束。
我听说过内存映射文件(http://msdn.microsoft.com/en-us/library/dd997372(v=vs.110).aspx),我想知道它们是否能解决我的问题。
感谢
使用内存映射的API是绝对可行的,它可能会执行得很好——当然,建议进行一些测试。
如果你想寻找一个可能的替代实施方案,我有以下建议。
-
创建一个静态堆栈数据结构,下载线程可以在下载后立即推送每个文件段。
-
让一个单独的线程侦听堆栈上的推送通知。弹出堆栈文件段,并以单线程方式将每个段保存到目标文件中。
按照上面的模式,通过在文件段的下载和保存之间放置一个堆栈容器,您已经将它们分离为一个常规文件。
根据堆栈处理的实现,您将能够用很少的线程锁定来实现这一点,这将最大限度地提高性能。
这样做的好处是,你可以100%控制正在发生的事情,并且解决方案可能更便携(如果这是一个问题的话)。
您所做的堆栈解耦模式也可以非常通用地实现,甚至可能在未来重用。
它的实现并没有那么复杂,可能与围绕内存映射api所需的实现不相上下。
玩得开心
/安德斯
到目前为止公布的答案当然是解决您的问题,但您也应该考虑到多线程I/O写入很可能不会提高性能。
多线程下载的原因是显而易见的,而且效果显著。不过,当你试图组合文件时,请记住,你让多个线程在传统硬盘上操作一个机械头。如果是SSD,您可能会获得更好的性能。
如果您使用单个线程,那么您就远远超过了HDD的写入容量。根据定义,这是写入约定磁盘的最快方式。
如果你不这么认为,我很想知道为什么。我宁愿集中精力通过调整缓冲区大小等来调整单个线程的写入性能。
是的,这是可能的,但您需要采取的唯一预防措施是控制没有两个线程在文件的同一位置进行写入,否则文件内容将不正确。
FileStream writeStream = new FileStream(destinationPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write);
writeStream.Position = startPositionOfSegments; //REMEMBER This piece of calculation is important
// A simple function to write the bytes ... just read from your source and then write
writeStream.Write(ReadBytes, 0 , bytesReadFromInputStream);
在每次Write
之后,我们使用writeStream.Flush();
,以便将缓冲的数据写入文件,但您可以根据需要进行更改。
由于您已经有了并行下载文件段的代码。您需要做的唯一更改就是打开上面发布的文件流,而不是在本地创建多个分段文件,只打开单个文件的流。
startPositionOfSegments
非常重要,它的计算非常完美,这样就不会有两个段将所需下载的字节覆盖到文件的同一位置,否则它将提供不正确的结果。
上面的程序在我们这边运行得很好,但如果您的分段大小太小,这可能会成为问题(我们也遇到了问题,但在增加分段大小后,问题得到了解决)。如果遇到任何异常,则也只能同步Write
部分。