You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#中拆分MemoryMappedFile为两个数据块时的写入异常问题

内存映射文件双Accessor读写冲突问题的原因与解决方法

问题现象

创建两个MemoryMappedViewAccessor分别访问共享内存的不同区域,控制台输出的指针虚拟地址不同,但读写操作时两个区块的数据互相覆盖,实际指向同一内存区域。

问题根源

Windows系统中,内存映射文件的视图是以内存页为单位分配的,默认页大小为4KB(可通过Environment.SystemPageSize获取):

  • 调用CreateViewAccessor时,若指定的起始偏移量未对齐到页边界,系统会自动将其调整至最近的页起始位置;
  • 若视图大小不足一页,系统会分配一整页的内存空间。

你的两个区块总大小仅30字节,远小于单页内存容量,因此两个MemoryMappedViewAccessor实际映射的是同一个物理内存页。虽然AcquirePointer返回的虚拟地址看似不同,但这些虚拟地址最终都指向该物理页的起始位置,导致读写操作互相覆盖。

解决方案

方案1:手动对齐偏移量到页边界

计算每个区块的起始偏移时,确保其为页大小的整数倍,同时预留足够的共享内存空间:

using System.IO.MemoryMappedFiles;

int pageSize = Environment.SystemPageSize;
const int block1Size = 20;
const int block2Size = 10;

// 对齐偏移量:block1从页起始位置开始,block2从下一个页起始位置开始
long block1Offset = 0;
long block2Offset = pageSize;

// 创建足够容纳两个对齐后区块的共享内存
MemoryMappedFile sharedFile = MemoryMappedFile.CreateNew("dummy_host_name", block2Offset + block2Size);

MemoryMappedViewAccessor block1Accessor = sharedFile.CreateViewAccessor(block1Offset, block1Size);
MemoryMappedViewAccessor block2Accessor = sharedFile.CreateViewAccessor(block2Offset, block2Size);

// 后续测试逻辑保持不变

方案2:使用Accessor内置读写方法(推荐)

MemoryMappedViewAccessor提供了WriteArray/ReadArray等内置方法,这些方法会自动处理偏移量,无需手动管理指针,从根源上避免页对齐问题:

using System.IO.MemoryMappedFiles;
using System.Linq;

const int block1Size = 20;
const int block2Size = 10;

MemoryMappedFile sharedFile = MemoryMappedFile.CreateNew("dummy_host_name", block1Size + block2Size);

MemoryMappedViewAccessor block1Accessor = sharedFile.CreateViewAccessor(0, block1Size);
MemoryMappedViewAccessor block2Accessor = sharedFile.CreateViewAccessor(block1Size, block2Size);

// 清除初始数据
byte[] zeros = new byte[block1Size];
block1Accessor.WriteArray(0, zeros, 0, block1Size);
zeros = new byte[block2Size];
block2Accessor.WriteArray(0, zeros, 0, block2Size);
PrintData(block1Accessor, block2Accessor);

// 写入第一个区块数据
byte[] firstDataInput = Enumerable.Range(1, block1Size).Select(i => (byte)i).ToArray();
block1Accessor.WriteArray(0, firstDataInput, 0, block1Size);
PrintData(block1Accessor, block2Accessor);

// 写入第二个区块数据
byte[] secondDataInput = Enumerable.Range(block1Size + 1, block2Size).Select(i => (byte)i).ToArray();
block2Accessor.WriteArray(0, secondDataInput, 0, block2Size);
PrintData(block1Accessor, block2Accessor);

static void PrintData(MemoryMappedViewAccessor block1, MemoryMappedViewAccessor block2)
{
    byte[] block1Data = new byte[block1Size];
    block1.ReadArray(0, block1Data, 0, block1Size);
    
    byte[] block2Data = new byte[block2Size];
    block2.ReadArray(0, block2Data, 0, block2Size);
    
    Console.WriteLine("first block: " + string.Join(", ", block1Data));
    Console.WriteLine("second block: " + string.Join(", ", block2Data));
}

内容的提问来源于stack exchange,提问作者dcidral

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 01:54:51