.Net多进程通信共享内存映射文件Memory Mapped

.Net多进程通信共享内存映射文件Memory Mapped的攻略

什么是Memory Mapped文件

Memory Mapped文件是一种通信机制,可以在多个进程之间共享数据,同时不需要进行大规模的内存拷贝,这个机制的核心是共享内存映射文件。

在Windows系统中,每个进程都有自己独立的虚拟内存空间,不同进程之间的虚拟内存空间是隔离的。但实际上,操作系统内部实现了一个内存映射机制(memory mapping),将虚拟内存地址和物理内存地址进行映射,实现了虚拟内存到物理内存的转换。利用这一机制,我们可以将同一个共享内存映射文件映射到多个进程的虚拟内存地址空间中。这样,不同进程就可以直接访问同一个内存区域,实现数据共享。

.Net中实现Memory Mapped文件

在.Net框架中,实现共享内存映射文件的方式就是MemoryMappedFile类。该类通过一些静态方法创建和打开内存映射文件,然后通过一些实例方法获取映射视图,从而访问共享内存。

创建内存映射文件

创建内存映射文件可以使用MemoryMappedFile类的CreateNew方法,这个方法会创建一个新的内存映射文件,如果文件已存在则会抛出异常。

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

try
{
    using (var mmf = MemoryMappedFile.CreateNew("test", 1024))
    {
        // 对读写权限的定义,需要进行类型转换
        using (var accessor = mmf.CreateViewAccessor(0, 1024, MemoryMappedFileAccess.ReadWrite))
        {
            // 将数据写入共享内存中
            accessor.Write(1, (byte)65); //写入A,byte类型
            accessor.Write(2, (byte)66); //写入B,byte类型
        }
    }
}
catch(IOException ex)
{
    //文件已存在
}

这个示例中创建了一个名为“test”的共享内存映射文件,大小为1024字节。使用CreateViewAccessor方法,可以获取到一个访问共享内存的对象,然后使用Write方法往共享内存中写入数据。

打开内存映射文件

如果内存映射文件已经存在,并且需要打开它来进行操作,可以使用MemoryMappedFile类的OpenExisting方法。这个方法会打开指定名称的内存映射文件,如果文件不存在则会抛出异常。

using System.IO.MemoryMappedFiles;

try
{
    using (var mmf = MemoryMappedFile.OpenExisting("test"))
    {
        // 对读写权限的定义,需要进行类型转换
        using (var accessor = mmf.CreateViewAccessor(0, 1024, MemoryMappedFileAccess.ReadWrite))
        {
            // 从共享内存中读取数据
            var chA = (char)accessor.ReadByte(1); //读取A
            var chB = (char)accessor.ReadByte(2); //读取B
        }
    }
}
catch (FileNotFoundException ex)
{
    //文件不存在
}

这个示例中打开了名称为“test”的共享内存映射文件,并从中读取数据。

示例1:实现两个进程之间的数据通信

在这个示例中,我们将创建两个不同的控制台应用程序,一个用来写入数据,另一个用来读取数据。这两个程序会通过一个内存映射文件来实现数据通信。

写入数据

using System.IO.MemoryMappedFiles;
using System.Threading;

class Program
{
    static void Main(string[] args)
    {
        try
        {
            // 当读取进程打开内存映射文件后,此处才执行
            using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting("test"))
            {
                Console.WriteLine("Successfully opened test");

                //对读写权限的定义,需要进行类型转换
                using (MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor(0, 1024, MemoryMappedFileAccess.ReadWrite))
                {
                    for (int i = 1; i <= 10; i++)
                    {
                        accessor.Write(i, (byte)i);
                        Console.WriteLine("Value {0} written", i);
                        Thread.Sleep(1000);
                    }
                }
            }
        }
        catch (FileNotFoundException ex)
        {
            Console.WriteLine("Memory-mapped file does not exist. Run Process A first, then B.");
        }
    }
}

这个程序将打开一个名为“test”的共享内存映射文件,并循环向文件中写入数据。

读取数据

using System;
using System.IO.MemoryMappedFiles;
using System.Threading;

class Program
{
    static void Main(string[] args)
    {
        try
        {
            using (MemoryMappedFile mmf = MemoryMappedFile.CreateOrOpen("test", 1024))
            {
                Console.WriteLine("Successfully created test");

                //对读写权限的定义,需要进行类型转换
                using (MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor(0, 1024, MemoryMappedFileAccess.ReadWrite))
                {
                    for (int i = 1; i <= 10; i++)
                    {
                        byte value;
                        accessor.Read(i, out value);
                        Console.WriteLine("Value read {0}", value);
                        Thread.Sleep(1000);
                    }
                }
            }
        }
        catch (FileNotFoundException ex)
        {
            Console.WriteLine("Memory-mapped file does not exist. Run Process A first, then B.");
        }
    }
}

这个程序将创建一个名为“test”的共享内存映射文件,并循环从文件中读取数据。

示例2:实现一个基于共享内存的循环队列

在这个示例中,我们将创建一个简单的循环队列并利用共享内存实现两个进程之间的数据队列操作。

实现共享内存队列

class SharedQueue
{
    // 队列最大长度
    public const int QueueLength = 8;

    // 共享内存映射对象
    private readonly MemoryMappedFile mmf;

    // 用于控制共享内存区域的访问权限
    private readonly Mutex mtx;

    // 读写指针
    private int readIndex = 0, writeIndex = 0;

    // 数据区域
    private readonly int[] data = new int[QueueLength];

    // 读指针属性
    public int ReadIndex
    {
        get
        {
            return readIndex;
        }
    }

    // 写指针属性
    public int WriteIndex
    {
        get
        {
            return writeIndex;
        }
    }

    // 构造函数,初始化内存映射文件和互斥量
    public SharedQueue()
    {
        // 创建或打开内存映射文件
        mmf = MemoryMappedFile.CreateOrOpen("SharedQueueMemoryMappedFile", data.Length * sizeof(int));

        // 创建互斥量
        mtx = new Mutex(false, "SharedQueueMutex");
    }

    // 向队列中添加一个元素
    public void Add(int value)
    {
        mtx.WaitOne();
        bool indexChanged = false;

        // 循环队列
        if ((writeIndex + 1) % QueueLength != readIndex)
        {
            data[writeIndex] = value;
            writeIndex = (writeIndex + 1) % QueueLength;
            indexChanged = true;
        }

        mtx.ReleaseMutex();

        // 如果写索引改变就通知读线程有数据更新
        if (indexChanged)
        {
            NotifyUpdate();
        }
    }

    // 从队列中取出一个元素
    public int Get()
    {
        mtx.WaitOne();
        bool indexChanged = false;
        int value = -1;

        // 判断队列是否为空
        if (readIndex != writeIndex)
        {
            value = data[readIndex];
            readIndex = (readIndex + 1) % QueueLength;
            indexChanged = true;
        }

        mtx.ReleaseMutex();

        // 如果读索引改变就通知写线程有数据更新
        if (indexChanged)
        {
            NotifyUpdate();
        }

        return value;
    }

    // 查询队列中是否有数据
    public bool HasData()
    {
        bool result;

        mtx.WaitOne();

        result = readIndex != writeIndex;

        mtx.ReleaseMutex();

        return result;
    }

    // 从共享内存中获取读写索引
    public void GetIndex(out int readIndex, out int writeIndex)
    {
        readIndex = 0;
        writeIndex = 0;
        mtx.WaitOne();
        readIndex = this.readIndex;
        writeIndex = this.writeIndex;
        mtx.ReleaseMutex();
    }

    // 通知队列更新
    private void NotifyUpdate()
    {
        //打开另一个进程的Mutex对象
        using (var mutex = Mutex.OpenExisting("SharedQueueMutex"))
        {
            //通知队列更新
            mutex.ReleaseMutex();
        }
    }
}

写进程代码

using System.Threading;

class Producer
{
    static void Main(string[] args)
    {
        //创建共享内存队列
        var queue = new SharedQueue();

        while (true)
        {
            for (int i = 0; i < SharedQueue.QueueLength; i++)
            {
                if (queue.HasData())
                {
                    // 如果队列中有数据,就先等待一会
                    Thread.Sleep(10);
                    continue;
                }

                // 向队列中添加一个元素
                queue.Add(i + 1);

                Thread.Sleep(1000);
            }
        }
    }
}

读进程代码

using System;
using System.Threading;

class Consumer
{
    static void Main(string[] args)
    {
        //创建共享内存队列
        var queue = new SharedQueue();

        while (true)
        {
            int readIndex, writeIndex;
            queue.GetIndex(out readIndex, out writeIndex);

            while (queue.HasData())
            {
                // 从队列中获取一个元素,并显示到控制台上
                var value = queue.Get();
                Console.WriteLine("Got value: {0}", value);
            }

            Thread.Sleep(1000);
        }
    }
}

本例中创建了一个名为“SharedQueueMemoryMappedFile”的共享内存映射文件,并创建一个“SharedQueueMutex”的命名互斥量,用于控制进程对共享内存区域的访问权限。然后实现了一个基于共享内存的循环队列。
在生产者进程中,实例化共享队列对象,并在循环中向队列中添加数据。在消费者进程中也同样实例化共享队列对象,并在循环中获取队列中的数据。在获取队列的元素时,需要先获取队列中的读写索引,防止读写指针改变带来的问题。

这是一个基本的实现,可以修改和扩展以满足特定的应用需求。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:.Net多进程通信共享内存映射文件Memory Mapped - Python技术站

(0)
上一篇 2023年6月3日
下一篇 2023年6月3日

相关文章

  • springMVC+velocity实现仿Datatables局部刷新分页方法

    我们将使用SpringMVC和Velocity作为模板引擎来实现仿Datatables局部刷新分页的功能。以下是详细的步骤: 第一步:配置SpringMVC 为了使用SpringMVC,我们需要添加如下依赖: <dependency> <groupId>org.springframework</groupId> <a…

    C# 2023年5月31日
    00
  • cnblogs csdn 代码运行框实现代码

    如果想在博客中展示代码的运行效果,可以借助一些第三方的代码运行框。像cnblogs和csdn都提供了这样的功能,可以直接在文章中展示代码的执行结果、输出或图形等,非常实用。下面是使用cnblogs和csdn实现代码运行框的攻略。 一、cnblogs 代码运行框实现 1. 准备 首先,需要在博客园中打开“源代码”模式,即切换到HTML源代码编辑模式,才能够使用…

    C# 2023年5月31日
    00
  • Oracle中的序列SEQUENCE详解

    Oracle中的序列SEQUENCE详解 简介 在Oracle数据库中,SEQUENCE是一种对象,可以用于生成唯一的数字序列。典型的用法包括生成主键ID,但它还可以用于其他用途,如生成订单号、交易号等。 一个SEQUENCE对象由三个主要的元素组成: 序列名:是用于标识该序列的名称,在创建SEQUENCE对象时必须指定该属性; 起始值:是该序列生成数字的初…

    C# 2023年5月15日
    00
  • .Net Core 集成 Kafka的步骤

    在本攻略中,我们将详细讲解如何在.Net Core中集成Kafka,并提供两个示例说明。 安装Kafka:首先,我们需要安装Kafka。我们可以从官方网站下载Kafka,并按照官方文档进行安装和配置。 安装Confluent.Kafka:接下来,我们需要安装Confluent.Kafka NuGet包。我们可以使用Visual Studio的NuGet包管理…

    C# 2023年5月16日
    00
  • c#中XML解析文件出错解决方法

    针对“c#中XML解析文件出错解决方法”的问题,我准备提供以下攻略: 1. 确认XML文件格式是否正确 在解析XML文件前首先要确定XML文件是否正确的格式,格式不正确会导致XML文件解析出错。 例如,下面这段XML文件: <?xml version="1.0" encoding="UTF-8" ?> &l…

    C# 2023年5月15日
    00
  • .Net Core 3.1 Web API基础知识详解(收藏)

    .Net Core 3.1 Web API基础知识详解攻略 在本攻略中,我们将深入讲解.Net Core 3.1 Web API的基础知识,并提供两个示例说明。 什么是.Net Core 3.1 Web API? .Net Core 3.1 Web API是一种基于RESTful架构的Web服务,用于提供数据和功能给客户端应用程序。它是使用.Net Core…

    C# 2023年5月17日
    00
  • asp.net 验证码生成和刷新及验证

    asp.net验证码生成 在asp.net中生成验证码需要使用Captcha控件,该控件可以生成图片验证码并且可以自定义验证码字符集合,大小,颜色等等。 首先需要在aspx页面中引入该控件: <%@ Register Assembly="System.Web.UI.WebControls" Namespace="Syste…

    C# 2023年6月1日
    00
  • .NET 6开发TodoList应用引入第三方日志库

    为了在.NET 6开发TodoList应用中引入第三方日志库,可以参考以下步骤: 步骤一:在TodoList项目中安装第三方日志库 可以使用NuGet包管理器或Package Manager Console安装第三方日志库。常见的日志库有Serilog、NLog、log4net等。以Serilog为例,可以在Package Manager Console中使…

    C# 2023年6月3日
    00
合作推广
合作推广
分享本页
返回顶部