VB.Net 进程间通信——内存映射文件

原文地址:https://zhuanlan.zhihu.com/p/39925519

温故之.NET进程间通信——内存映射文件

 

上一篇技术文章中,我们讲解了进程间通信中的管道通信方式,这只是多种进程间通信方式中的一种,这篇文章我们回顾一下另一种进程间通信的方式——内存映射文件

基础概念

Windows 提供了 3 种进行内存管理的方法:

  • 虚拟内存:适合用来管理大型对象或结构数组
  • 内存映射文件:适合用来管理大型数据流(通常来自文件),也适合在单机上多个进程(运行着的进程)之间共享数据
  • 内存堆栈:适合用来管理大量的小对象

内存映射文件在 Windows 中使用场景很多,进程间通信也只是其多个应用场景中的一个。它在操作大文件时非常高效,这种场景下也使用得非常广泛。比如数据库文件

借助文件和内存空间之间的这种映射,应用可以直接对内存执行读写操作,从而间接的修改文件。自 .NET Framework 4 起(在 System.IO.MemoryMappedFiles 命名空间下),我们便可以通过托管代码去访问内存映射文件

如果我们需要使用内存映射文件,则必须创建该内存映射文件的视图(该视图映射到文件的全部内存或一部分内存上)。我们也可以为内存映射文件的同一部分创建多个视图,从而创建并发内存。若要让两个视图一直处于并发状态,必须通过同一个内存映射文件创建它们。当文件大于可用于内存映射的应用逻辑内存空间(在 32 位计算机中为 2GB)时,也有必要使用多个视图

视图分为以下两种类型:流访问视图和随机访问视图

  • 使用流访问视图,可以顺序访问文件。建议对非持久化文件和 IPC 使用这种类型(通过 MemoryMappedFile.CreateViewStream 创建此视图)
  • 随机访问视图是处理持久化文件的首选类型(通过 MemoryMappedFile.CreateViewAccessor 创建此视图)

内存映射文件通过操作系统的内存管理程序进行访问,因此文件会被自动分区到很多页面,并根据需要进行访问(即自动的内存管理,不需要我们人为干预)

内存映射文件分为两种类型:持久化内存映射文件和非持久化内存映射文件,不同的类型应用于不同的场景

持久化内存映射文件

持久化文件是与磁盘上的源文件相关联的内存映射文件(即磁盘上需要有个文件才行)。当最后一个进程处理完文件时,数据保存到磁盘上的源文件中。此类内存映射文件适用于处理非常大的源文件,这种方式在很多数据库中都有使用

可使用 MemoryMappedFile.CreateFromFile 创建此类型的映射文件。要想访问此类型的映射文件,可通过 MemoryMappedFile.CreateViewAccessor 创建一个随机访问视图。这也是访问持久化内存映射文件推荐的方式

示例代码如下

using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;

namespace App {
    class Program {
        static void Main(string[] args) {
            long offset = 0x0000;
            long length = 0x2000;  // 8K

            string mapName = "Demos.MapFiles.TestInstance";
            int colorSize = Marshal.SizeOf(typeof(Color));
            long number = length / colorSize;
            Color color;

            // 从磁盘上现有文件,创建内存映射文件,第三个参数为这个内存映射文件的名称
            var firstMapFile = MemoryMappedFile.CreateFromFile(@"d:\test_data.data", FileMode.OpenOrCreate, mapName);
            // 创建一个随机访问视图
            using (var accessor = firstMapFile.CreateViewAccessor(offset, length)) {
                // 更改映射文件内容
                for (long i = 0; i < number; i += colorSize) {
                    accessor.Read(i, out color);
                    color.Add(new Color() { R = 10, G = 10, B = 10, A = 10 });
                    accessor.Write(i, ref color);
                }
            }

            // 打开已经存在的内存映射文件
            // 第一个参数为这个内存映射文件的名称
            // 【此处的代码可以放在另一个进程中】
            var secondMapFile = MemoryMappedFile.OpenExisting(mapName);
            using (var secondAccessor = secondMapFile.CreateViewAccessor(offset, length)) {
                // 读取映射文件内容
                for (long i = 0; i < number; i += colorSize) {
                    secondAccessor.Read(i, out color);
                    Console.WriteLine(color);
                }
            }

            Console.ReadLine();

            // 释放内存映射文件资源
            firstMapFile.Dispose();
            secondMapFile.Dispose();
        }
    }
    // 为了便于测试,创建一个简单的结构
    
上一篇:css-书写规范


下一篇:uni-appios白屏问题