我有一个大磁盘文件(大约8 GB),包含我需要读取的几百万条记录,在内存中处理并写回另一个文件。所有记录都是固定长度(例如,100字节)。
我正在考虑将我的进程并行化以在多个线程(通常是4-8)上运行,每个线程将(唯一地)分配要处理的文件的特定部分(例如,1 GB块)。由于每个线程都会限制其对已分配文件部分的读写,因此我的代码不存在种族危险的风险。
我是否可以初始化多个线程,每个线程都有自己的FileStream
,用于读取/写入同一个文件而不会锁定,而不会有损坏的风险?假设目标文件已提前扩展到其完整大小(使用FileStream.SetLength
),并且在打开每个FileShare
时指定了相应的FileStream
标志。
另外,如果多个线程同时访问同一个文件,是否会因缓冲丢失而导致速度降低?我关注FileStream
class上的MSDN文档中的“检测流位置更改”部分,其中指出:
当
FileStream
对象的句柄没有独占保留时,另一个线程可以同时访问文件句柄并更改与文件句柄关联的操作系统文件指针的位置。 [...]如果在调用
Read
方法时检测到句柄位置发生意外更改,.NET Framework将丢弃缓冲区的内容并再次从文件中读取流。这可能会影响性能,具体取决于文件的大小以及可能影响文件流位置的任何其他进程。
这是否适用于我的情况,或者FileStream
个实例创建的文件句柄是否独立且独立,即使访问同一个文件也是如此?
答案 0 :(得分:6)
这是非常安全的。
MSDN文章中没有提到问题的风险,因为它仅适用于您自己更改底层句柄的情况。你根本没有访问句柄。
您会注意到随机磁盘IO虽然会破坏性能。您可能希望通过从文件中读取大块(16MB左右)并使用锁来防止并发读写调用来缓解这种情况。请注意,即使在不同的FileStream实例上也需要防止并发调用,因为操作系统不会原子地处理IO。在内部,它们被分成小尺寸,以实现公平性和可预测的延迟。这会导致随机IO。
为什么不创建一个读取器线程将缓冲区推入BlockingCollection?您可以在多个线程上使用Parallel.ForEach处理该集合。
答案 1 :(得分:2)
“内存映射文件将文件内容映射到应用程序的逻辑地址空间。内存映射文件使程序员能够处理非常大的文件,因为可以同时管理内存,并且它们允许完全随机访问无需搜索的文件。内存映射文件也可以在多个进程之间共享。
CreateFromFile方法从指定路径或磁盘上现有文件的FileStream创建内存映射文件。在取消映射文件时,更改会自动传播到磁盘。
CreateNew方法创建一个未映射到磁盘上现有文件的内存映射文件;并且适用于为进程间通信(IPC)创建共享内存。
内存映射文件与名称相关联。
您可以创建内存映射文件的多个视图,包括文件各部分的视图。您可以将文件的同一部分映射到多个地址以创建并发内存。要使两个视图保持并发,必须从同一个内存映射文件创建它们。使用两个视图创建同一文件的两个文件映射不提供并发性。“
http://msdn.microsoft.com/en-us/library/system.io.memorymappedfiles.memorymappedfile.aspx