Skip to content

.NET Core 实战 [No.263~264] 内存映射文件

🏷️ 《.NET Core 实战》

内存映射文件(MemoryMappedFile),其实是在应用程序内存空间中划分的一块特殊内存,可以像操作磁盘文件那样,在内存中新建文件,并写入或读取内容。

内存映射文件可以用于在进程之间共享数据。下面的两段代码处于两个不同的项目中,分别用于写入和读取内存映射文件。

csharp
MemoryMappedFile file = MemoryMappedFile.CreateNew("test", 200L);
using (MemoryMappedViewStream mmvstream = file.CreateViewStream())
using (StreamWriter writer = new StreamWriter(mmvstream))
{
    writer.WriteLine("Hi. Welcome to my blog<www.liujiajia.me>.");
}
Console.WriteLine("文件已写入内存");
Thread.Sleep(TimeSpan.FromSeconds(10));
csharp
Console.WriteLine("等待读取文件");
Thread.Sleep(TimeSpan.FromSeconds(5));
using (MemoryMappedFile file = MemoryMappedFile.OpenExisting("test"))
using (MemoryMappedViewStream vstream = file.CreateViewStream())
using (StreamReader reader = new StreamReader(vstream))
{
    string str = reader.ReadLine();
    Console.WriteLine(str);
}
Console.WriteLine("按 Enter 键退出");
Console.ReadLine();

同时运行可以看到跨进程共享数据的效果。输出结果如下:

在内存中创建的文件,当引用该内存的最后一个进程退出时,内存中的数据也会被清理并由系统回收。

通过关联磁盘文件和内存映射文件,可以在内存映射文件被回收时,自动写入到磁盘文件。

csharp
// 建立磁盘文件和内存文件之间的关联并写入四个数值
using (MemoryMappedFile file = MemoryMappedFile.CreateFromFile("demo.data", FileMode.OpenOrCreate, "demo", 100L))
using (MemoryMappedViewStream vstream = file.CreateViewStream())
using (BinaryWriter writer = new BinaryWriter(vstream))
{
    writer.Write(157);
    writer.Write(1.18f);
    writer.Write(1990L);
    writer.Write(8.16d);
}
// 从文件中依次读取这四个数值
using (FileStream stream = File.OpenRead("demo.data"))
using (BinaryReader reader = new BinaryReader(stream))
{
    Console.WriteLine($"文件大小:{stream.Length}");
    Console.WriteLine($"读到的 int 值:{reader.ReadInt32()}");
    Console.WriteLine($"读到的 float 值:{reader.ReadSingle()}");
    Console.WriteLine($"读到的 long 值:{reader.ReadInt64()}");
    Console.WriteLine($"读到的 double 值:{reader.ReadDouble()}");
}

内存映射文件还可以映射文件的 一段 数据,这个功能在读写大型文件时很有用。

下面的示例继续使用上面关联的磁盘文件,通过映射部分数据的方式来读取写入的第二和第三个数值。

csharp
// 从文件中读取中间的两个数值
long offset = Marshal.SizeOf(typeof(int)); // 4
long length = Marshal.SizeOf(typeof(float)) + Marshal.SizeOf(typeof(long)); // 4 + 8
using (MemoryMappedFile file = MemoryMappedFile.CreateFromFile("demo.data", FileMode.Open, "demo-part"))
using (MemoryMappedViewAccessor accessor = file.CreateViewAccessor(offset, length))
{
    Console.WriteLine($"读到的 float 值:{accessor.ReadSingle(0)}");
    Console.WriteLine($"读到的 long 值:{accessor.ReadInt64(Marshal.SizeOf(typeof(float)))}"); // position = 4
}

上面两段代码的运行结果如下:


参考:《.NET Core 实战:手把手教你掌握 380 个精彩案例》 -- 周家安 著