5

我有一个大磁盘文件(大约 8 GB),其中包含我需要读取、在内存中处理和写回另一个文件的几百万条记录。所有记录的长度都是固定的(比如 100 字节)。

我正在考虑将我的进程并行化以在多个线程(通常为 4-8 个)上运行,每个线程将(专门)分配文件的特定部分来处理(例如,1 GB 块)。由于每个线程都会将其读取和写入限制在分配给它的文件部分,因此我的代码不存在竞争风险。

我是否允许初始化多个线程,每个线程都有自己的FileStream,以便在没有锁定的情况下读取/写入同一个文件,而不会有损坏的风险?假设目标文件已经提前(使用FileStream.SetLength)扩展至其完整大小,并且FileShare在打开每个FileStream.

另外,如果多个线程同时访问同一个文件,我是否会因缓冲丢失而面临减速的风险?我担心 MSDN 文档中关于FileStreamclass的“Detection of stream position changes”部分,其中指出:

当一个FileStream对象的句柄没有独占保留时,另一个线程可以同时访问文件句柄并更改与文件句柄关联的操作系统文件指针的位置。[…]

如果在对该方法的调用中检测到句柄位置发生意外更改Read,.NET Framework 将丢弃缓冲区的内容并再次从文件中读取流。这可能会影响性能,具体取决于文件的大小以及可能影响文件流位置的任何其他进程。

这是否适用于我的情况,或者由FileStream实例创建的文件句柄是否不同且独立,即使访问同一个文件?

4

2 回答 2

6

这是完全安全的。

MSDN 文章中提到的问题没有风险,因为它仅适用于您自己更改底层句柄的情况。您根本没有访问句柄。

您会注意到随机磁盘 IO 会破坏性能。您可能希望通过从文件中读取大块(16MB 左右)并使用锁来防止并发读取和写入调用来缓解这种情况。请注意,即使在不同的 FileStream 实例上,您也需要防止并发调用,因为操作系统不会自动处理 IO。在内部,它们被分成小尺寸,以实现公平和可预测的延迟。这会导致随机 IO。

为什么不创建一个将缓冲区推送到 BlockingCollection 的读取器线程?您可以在多个线程上使用 Parallel.ForEach 处理该集合。

于 2012-06-23T12:25:04.773 回答
3

“内存映射文件将文件的内容映射到应用程序的逻辑地址空间。内存映射文件使程序员能够处理非常大的文件,因为内存可以同时管理,并且它们允许在没有需要查找。内存映射文件也可以跨多个进程共享。

CreateFromFile 方法从指定路径或磁盘上现有文件的 FileStream 创建内存映射文件。当文件未映射时,更改会自动传播到磁盘。

CreateNew 方法创建一个未映射到磁盘上现有文件的内存映射文件;并且适用于为进程间通信(IPC)创建共享内存。

内存映射文件与名称相关联。

您可以创建内存映射文件的多个视图,包括文件部分的视图。您可以将文件的同一部分映射到多个地址以创建并发内存。为了使两个视图保持并发,它们必须从同一个内存映射文件创建。使用两个视图创建同一文件的两个文件映射不提供并发性。”

http://msdn.microsoft.com/en-us/library/system.io.memorymappedfiles.memorymappedfile.aspx

于 2012-06-23T12:15:52.617 回答