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

相关文章

  • C#编程实现带有Aero效果的窗体示例

    C#编程实现带有Aero效果的窗体示例 介绍 Aero是Windows Vista中引进的一个用户界面特性,主要是增强用户界面的可观性和交互性,如透明度、窗口预览、任务栏缩略图等。在C#编程中,我们可以通过代码实现带有Aero效果的窗体,提高程序的用户体验。 本文将详细说明如何通过C#编程实现带有Aero效果的窗体,包含两条示例说明。 实现步骤 步骤1:安装…

    C# 2023年5月31日
    00
  • 在C#中使用OpenCV(使用OpenCVSharp)的实现

    在C#中使用OpenCV实现图像处理功能,可以使用OpenCVSharp库。以下是使用OpenCVSharp的攻略: 步骤一:安装OpenCVSharp库 首先在你的项目中安装OpenCVSharp库。可以通过NuGet安装方式,或者在其官网下载dll文件或源代码手动添加到项目中。 步骤二:引用命名空间 在所需要使用OpenCVSharp库的类文件中,引用命…

    C# 2023年6月1日
    00
  • C#构建树形结构数据(全部构建,查找构建)

    C#构建树形结构数据(全部构建,查找构建) 前言 树形结构数据在实际开发中非常常见,具有分级、层级、分类等特点,通常应用于目录结构、组织机构、商品分类等场景。本文将介绍如何使用C#构建树形结构数据,包括全部构建和查找构建两种方案。 全部构建 步骤一:定义数据结构 我们假设有一个数据表,包含id、parentId、name三个字段,其中id为自增主键,pare…

    C# 2023年5月31日
    00
  • asp.net 面试+笔试题目

    首先,需要明确“asp.net 面试+笔试题目”主要考察的是asp.net的技能应用和基础知识掌握程度。在应对这类面试+笔试题目时,需要注意以下几个方面: 准备基础知识和技能 需要提前准备相关的asp.net基础知识、技能和编程经验。可以通过阅读相关文献、官方文档、参加课程、以及进行实践等多种方式来提高技能水平。 熟悉面试题型 需要了解面试题目的常见类型,例…

    C# 2023年5月31日
    00
  • abp(net core)+easyui+efcore实现仓储管理系统——组织管理升级之下(六十二)

    Abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统——ABP总体介绍(一) abp(net core)+easyui+efcore实现仓储管理系统——解决方案介绍(二) abp(net core)+easyui+efcore实现仓储管理系统——领域层创建实体(三)…

    C# 2023年4月24日
    00
  • c#完美截断字符串代码(中文+非中文)

    C#完美截断字符串代码(中文+非中文)攻略 概述 在C#中,对一个字符串进行截断,即取其中一部分,是一个比较常见的操作。本文将介绍一种完美的字符串截断方法,支持中文和非中文的字符串。 方法 string SubstringSmart(string str, int length) { if (string.IsNullOrEmpty(str)) { retu…

    C# 2023年5月31日
    00
  • C# 实例解释面向对象编程中的单一功能原则(示例代码)

    针对您的问题,以下是C# 实例解释面向对象编程中的单一功能原则的攻略及示例代码。 单一功能原则 单一功能原则(Single Responsibility Principle,SRP)是面向对象编程中的一项核心原则,其核心思想是一个类或模块只负责一项职责(也就是只有一个引起它变化的原因)。这样可以让代码更加易于维护、修改和测试。举例来说,如果一个类负责多项职责…

    C# 2023年6月1日
    00
  • c#生成站点地图(SiteMapPath)文件示例程序

    当我们在建立一个网站时,通常需要建立一个站点地图文件 (SiteMapPath) 来帮助用户更好的理解我们网站的目录结构,帮助用户更好的导航。 下面我将详细讲解如何用 C# 生成站点地图文件,同时提供两个示例程序: 站点地图文件的基础 什么是站点地图文件 站点地图文件是一种可供搜索引擎和网站访问者使用的页面列表,其中包含了站点中的所有页面和与每个页面相关的元…

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