C#原生MemoryMappedFile无法跨进程读取映射内存,Win32方法正常
问题描述
我有一个持续运行循环的任务线程,通过内存映射文件执行读取操作。当其他应用向该内存映射文件写入数据时,使用Win32内存映射文件方法的线程可以正常读取到数据,但使用C#原生MemoryMappedFile方法的线程却无法读取到这些数据。
可正常工作的Win32实现代码
IntPtr _iFileMappingHandle, _iFileMapStartAddress; _iFileMappingHandle = Win32.CreateFileMapping(Win32.InvalidFileHandle, 0, Win32.FileProtection.ReadWrite, 0, (uint)Size, Name); _iFileMapStartAddress = Win32.MapViewOfFile(_iFileMappingHandle, Win32.FileRights.ReadWrite, 0, 0, 0); var autoReset = new EventWaitHandle(true, EventResetMode.AutoReset, LockName); if (autoReset.WaitOne(5000)) { byte[] bTemp = new byte[Size]; Marshal.Copy(_iFileMapStartAddress, bTemp, 0, 512);
无法正常读取的C#原生实现代码
var mmf = MemoryMappedFile.CreateOrOpen(Name , Size , MemoryMappedFileAccess.ReadWrite); var viewSteam = mmf.CreateViewStream(0, Size); var autoReset = new EventWaitHandle(true, EventResetMode.AutoReset, LockName); while (!cancellationToken.IsCancellationRequested) { if (autoReset.WaitOne(5000)) { byte[] bTemp = new byte[Size]; viewSteam.Read(bTemp, 0, Size);
问题原因及解决方法
核心问题在于**ViewStream的读取位置未重置**:第一次调用Read后,流的位置指针会停在读取完成的位置,后续Read会从该位置开始,而非内存映射的起始地址;而Win32的Marshal.Copy是直接从映射内存的起始地址拷贝,不存在位置指针问题。
解决方法一:重置流的读取位置
在每次调用Read前,将流的位置设为0,确保从内存映射的起始处读取:
if (autoReset.WaitOne(5000)) { byte[] bTemp = new byte[Size]; viewSteam.Position = 0; // 重置读取位置到起始点 viewSteam.Read(bTemp, 0, Size); }
解决方法二:使用MemoryMappedViewAccessor替代ViewStream
MemoryMappedViewAccessor提供了更接近Win32方式的直接内存访问,无需处理流的位置问题:
var mmf = MemoryMappedFile.CreateOrOpen(Name, Size, MemoryMappedFileAccess.ReadWrite); using var accessor = mmf.CreateViewAccessor(0, Size); var autoReset = new EventWaitHandle(true, EventResetMode.AutoReset, LockName); while (!cancellationToken.IsCancellationRequested) { if (autoReset.WaitOne(5000)) { byte[] bTemp = new byte[Size]; accessor.ReadArray(0, bTemp, 0, Size); } }
额外注意事项
- 跨进程访问时,内存映射文件的名称、大小必须完全一致,否则会创建独立的映射对象。
- 同步用的
EventWaitHandle名称需保证唯一性,避免与系统中其他对象冲突。
内容的提问来源于stack exchange,提问作者steve
相关产品推荐
相关产品推荐

