如何在内存映射文件中进行并行处理数据

13

作为一个内存映射文件的名称表明,我理解使用C#中的MemoryMappedFile类可以将大文件的一部分映射到内存中以进行快速数据处理。我想要做的是并行处理内存映射文件。为了达到这个目的,我有以下问题:

  1. MemoryMappedFileViewAccessor是否线程安全和Parallel.For安全? 我实际上编写了一个演示程序来测试这个问题,它似乎工作正常。但找不到任何关于这个的参考资料。如果答案是肯定的,我就做好了。否则,
  2. 是否有直接使用数组访问内存映射的方法? 我知道MemoryMappedFileViewAccessor具有ReadArray方法,但使用该方法会复制内存。

你的并行处理是否涉及修改文件,还是仅从中读取? - Charles Lambert
@CharlesLambert 不,数据处理涉及到accessor.Write或accessor.Read操作,基本上是访问内存映射。内存映射文件实际上负责文件操作。 - Tae-Sung Shin
3个回答

12
您可以自行推理。内存映射文件只是程序中的一块内存,其字节可以被多个进程访问。因为这个块存在于特定地址,所以在托管代码中使用它们相当麻烦。这需要使用指针来访问数据,而在托管代码中使用它们是禁忌的。MemoryMappedFileViewAccessor包装了该指针,它将数据从托管内存复制到共享内存中。请注意,这击败了使用MMFs的主要原因,以及为什么它们的支持花费了很长时间才出现在.NET中。确保您不想改用命名管道。

因此,由于这是共享内存,MMF肯定不是线程安全的设计。如果线程读取和写入共享内存的相同部分,则会发生与在代码中全局变量相同的问题。您也必须以完全相同的方式保护它们,即使用锁确保只有一个线程可以访问共享部分。

还要注意,您需要在读取和写入MMF的进程之间实现该锁定。这往往是很痛苦的,您必须使用“主”进程创建并且“从”进程打开的命名互斥体。您不能在此锁定要求上精打细算。需要注意的是,您在问题中从未提到过要处理这个问题,所以要注意。

在一个进程内,不访问MMF相同部分的线程不会相互干扰。就像访问不同变量的两个线程不需要任何同步一样。只要它们拥有确保另一个进程无法写入该部分的互斥体即可。

请注意,这可能意味着您想使用Semaphore来保护MMF访问,Mutex只能被一个线程获取。


2
在内部,MemoryMappedViewAccessor源于UnmanagedMemoryAccessor,它看起来是不可变的,虽然没有只读字段——至少在读/写操作期间不会修改现有字段,这使其线程安全。反过来,它从SafeBuffer类中读取内存映射文件数据,注释头中包含以下文本:
/* Keep the penalties for using this class small, both in terms of space 
// and time.  Having multiple threads reading from a memory mapped file
// will already require 2 additional interlocked operations.  If we add in 
// a "current position" concept, that requires additional space in memory and 
// synchronization.  Since the position in memory is often (but not always)
// something that can be stored on the stack, we can save some memory by 
// excluding it from this object.  However, avoiding the need for
// synchronization is a more significant win.  This design allows multiple
// threads to read and write memory simultaneously without locks (as long as
// you don't write to a region of memory that overlaps with what another 
// thread is accessing).

所以我的猜测是,使用内存映射文件的操作是线程安全的,尽管在MSDN中没有确认这一点是很奇怪的。

0

1) MSDN声称,MemoryMappedFileMemoryMappedViewAccessor的所有公共静态成员(在Visual Basic中为Shared)都是线程安全的。但实例成员的线程安全性无法保证。

2) MemoryMappedFile的目的并不是为了减少内存分配。如果你从磁盘读取文件,你必须分配内存来存储读出文件中的数据。使用内存映射文件也是同样的情况。


@Thomas 内存映射文件仍然会占用地址空间。实际上,一个未命名的内存映射文件与相同粒度的低级操作系统内存分配并没有太大的区别。 - Neil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接