.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日

相关文章

  • asp.net(C#)实现功能强大的时间日期处理类完整实例

    asp.net(C#)实现功能强大的时间日期处理类完整实例 引言 在程序开发过程中,日期和时间处理是非常常见且重要的操作,然而C#在对时间日期的处理上提供了很多内置的功能和类,但是并不一定适合所有的场景。因此,本文介绍如何实现功能强大的时间日期处理类,让我们的开发变得更加高效。 步骤 创建一个新的C#类,在类中定义所需要的方法和变量。 “`csharp p…

    C# 2023年6月1日
    00
  • C#如何调用MFC 窗口 DLL

    要在C#中调用MFC窗口DLL,需要通过以下步骤: 1.创建MFC动态连接库(DLL)项目使用Visual Studio创建MFC动态连接库项目。在创建向导中选择“动态库”,然后选择“MFC应用程序向导”和“共享DLL”。在下一步中,请设置您的项目名称和位置,然后选择“在静态库和动态库之间共享MFC”。在下一步中,请选择“空项目”。 2.编写DLL代码在MF…

    C# 2023年6月6日
    00
  • asp.net javascript 文件无刷新上传实例代码

    下面是关于“asp.net javascript 文件无刷新上传实例代码”的详细攻略。 简介 asp.net javascript 文件无刷新上传是一种实现无需页面刷新即可上传文件的方法。它使用了 AJAX 技术并结合了 ASP.NET 的后台处理功能,使得文件上传变得更加简单和方便。 实现步骤 1. 前端页面 首先,在前端页面上需要设置一个表单,其中包括一…

    C# 2023年5月31日
    00
  • ASP.NET MVC格式化日期

    当我们开发ASP.NET MVC应用程序时,经常需要处理日期和时间数据,比如从数据库中读取日期数据并在页面上显示出来,或者从前端用户输入的日期字符串中解析出日期时间。 为了格式化日期,ASP.NET MVC中提供了多种处理方式,可以通过全局配置和局部配置来进行设置。 全局配置 如果你希望在整个应用程序中都使用同样的日期格式,可以在应用程序启动时进行全局配置。…

    C# 2023年5月31日
    00
  • C#中string和StingBuilder内存中的区别实例分析

    下面是“C#中string和StringBuilder内存中的区别实例分析”的完整攻略: 1. 什么是String和StringBuilder 在C#中,String和StringBuilder都是字符串类型。String是一个不可更改的字符串类型,而StringBuilder是一个可变的字符串类型。 2. String和StringBuilder的区别 2…

    C# 2023年6月8日
    00
  • c#静态方法和非静态方法详细介绍

    下面是关于”C#静态方法和非静态方法详细介绍”的完整攻略。 什么是静态方法和非静态方法 C#中的方法可以分为静态方法和非静态方法。 静态方法定义在类中,可以直接通过类名来调用。非静态方法定义在类中,必须通过对象来调用。 以下是一个简单的示例,演示了一个类中包含一个静态方法和一个非静态方法: public class MyClass { public stat…

    C# 2023年6月7日
    00
  • C#中Clone一个对象的值到另一个对象案例

    下面是C#中克隆一个对象的值到另一个对象的完整攻略: 1. 首先,什么是克隆? 在C#中,克隆通常指将一个对象完整地复制到另一个对象的操作。在克隆操作中,被复制的对象称为源对象,要克隆到的对象称为目标对象。源对象和目标对象通常都是同一种类型的对象,并且它们的属性或字段也应该是相同的。 2. Implement ICloneable 在C#中,可以通过实现IC…

    C# 2023年6月1日
    00
  • C# Directory.GetFiles(string path):获取指定目录下的所有文件路径

    Directory.GetFiles(string path) 是C#中的一个静态方法,它返回指定目录中文件的名称,包括该目录中的所有子目录。它是 DirectoryInfo 类的一个实例方法 GetFiles 的静态等效方法。 方法签名 public static string[] GetFiles(string path); public static …

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