C#使用共享内存实现进程间的通信

 C# 进程通信系列

第一章 共享内存(本章)

第二章 条件变量
第二章 消息队列


进程通信有多种方式,比如socket、管道、共享内存。c#直接提供了共享内存的相关库,但直接使用起来还是不太方便,需要使用Marshal处理内存对齐以及托管非托管转换的问题,本文提供一种,将上述操作包装,借助反射的手段,实现通过类和属性的方式使用共享内存。

一、.net共享内存对象

MemoryMappedFile是.net的共享内存对象,一般通过MemoryMappedFile.CreateNew的方式创建共享内存,最简单的创建方式是,传入名称及大小。

       //申请共享内存
       MemoryMappedFile mmf = MemoryMappedFile.CreateNew(name, size);

写共享内存,在指定位置,通过数组写入数据。

 using (var accessor = mmf.CreateViewAccessor(0, Size))
       {
        accessor.WriteArray(field.Position, array, 0, array.Length);
       }

读共享内存,在指定位置读取数据到数组。

using (var accessor = mmf.CreateViewAccessor(0, Size))
      {
       accessor.ReadArray(field.Position, buffer, 0, buffer.Length);
      }

二、定义上层共享内存对象

1、共享内存对象

 class SharedMemory : IDisposable
    {
        /// <summary>
        /// 共享内存名称(唯一标识)
        /// </summary>
        public string Name { private set; get; }
        /// <summary>
        /// 大小
        /// </summary>
        public long Size { private set; get; }
        /// <summary>
        /// 创建共享内存
        /// </summary>
        /// <param name="name">共享内存名称(唯一标识)</param>
        /// <param name="sharedMemoryFieldsMapping">映射实体对象</param>
        /// <returns>共享内存对象</returns>
        public static SharedMemory Create(string name, ISharedMemoryPropertiesMapping sharedMemoryFieldsMapping);
        /// <summary>
        /// 关闭共享内存
        /// </summary>
        public void Close();
        /// <summary>
        /// 关闭共享内存
        /// </summary>
        public void Dispose();
}

2、映射实体接口

继承此接口的类即可以作为共享内存的映射数据。

    //共享内存映射接口,实现此接口的对象其属性可以直接与共享内存映射。
    //规则是,属性类型只支持结构体以及byte[]数组
    //属性排列顺序即内存排列顺序
    interface ISharedMemoryPropertiesMapping
    {
        event Action<object, SharedMemoryPropertyChangedEventArgs> PropertyChanged;
        event Func<string, object> GetPropertyValue;
    }
    public class SharedMemoryPropertyChangedEventArgs
    {
        public object Value { set; get; }
        public string FieldName { set; get; }
    }
    //内存映射接口简化实现
   abstract class SharedMemoryFieldsMapping : ISharedMemoryPropertiesMapping
    {
        protected void NotifyFieldChanged(object value, [System.Runtime.CompilerServices.CallerMemberName] string filedName = "");     
        protected object NotifyGetFieldValue(object defalut, [System.Runtime.CompilerServices.CallerMemberName]string filedName = "");
        public event Action<object, SharedMemoryPropertyChangedEventArgs> PropertyChanged;
        public event Func<string, object> GetPropertyValue;
    }

三、使用方法

通过上述方式实现的上层共享内存对象,使用共享内存将变得非常简单。只需如下步骤:

1、定义共享内存实体

继承SharedMemoryFieldsMapping,定义需要的属性,set调用NotifyFieldChanged传入value,get调用NotifyGetFieldValue传入属性类型的缺省值 ,如下所示:

    class SharedData : SharedMemoryFieldsMapping
    {
        public double CurentTime{set{ NotifyFieldChanged(value);}get{return (double)NotifyGetFieldValue(double.NaN);} }
        public double Duration { set { NotifyFieldChanged(value); } get { return (double)NotifyGetFieldValue(double.NaN); } }
        public int IsPause { set { NotifyFieldChanged(value); } get { return (int)NotifyGetFieldValue(1); } }
        public int IsMute { set { NotifyFieldChanged(value); } get { return (int)NotifyGetFieldValue(1); } }
        public int Heartbeat { set { NotifyFieldChanged(value); } get { return (int)NotifyGetFieldValue(0); } }
        public int Flag { set { NotifyFieldChanged(value); } get { return (int)NotifyGetFieldValue(0); } }
        public int Length { set { NotifyFieldChanged(value); } get { return (int)NotifyGetFieldValue(0); } }
        [SharedMemeory(Size = 1024)]
        public byte[] Input { set { NotifyFieldChanged(value); } get { return (byte[])NotifyGetFieldValue(null); } }
        [SharedMemeory(Size = 1024)]
        public byte[] Output { set { NotifyFieldChanged(value); } get { return (byte[])NotifyGetFieldValue(null); } }

    }

注:(1)、属性的类型必须是值类型即元类型或结构体,引用类型只支持数组且必须设置数长度。

       (2)、属性排列顺序即内存排列顺序

       (3)、当前版本内存对齐为1,在c++中的内存对齐需要设置为#pragma pack(1)

上述对象在c++程序中可以直接使用如下数据结构对应:

typedef struct {
	double CurentTime;//当前时间
	double Duration;//总时长
	int IsPause;
	int IsMute;
	int Heartbeat;//心跳
	int Flag;
	int Length;
	char Input[1024];
	char Output[1024];
}SharedData;

2、创建共享内存

初始化一个共享内存实体对象,然后调用SharedMemory.Create,传入共享内存的名称以及共享内存实体对象即创建完成,此后直接读写实体的属性即是在读写共享内存。

SharedData dataShared = new SharedData();
var sharedMemory = SharedMemory.Create(sharedName, dataShared);

3、销毁共享内存

使用完成后需要销毁共享内存,否则可能会一直存在操作系统中,就算进程退出,共享内存可能依然存在。

sharedMemory.Dispose();

四、使用实例

这里的实例只用了互斥锁来控制资源有序访问,真实使用场景一般是需要用条件变量或者信号量来做访问控制的。下面是具体代码:

1、父进程

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
namespace testshared
{
    class Program
    {

        //内存映射的实体 属性排列顺序与内存排列直接相关
        class SharedData : SharedMemoryFieldsMapping
        {
            public double CurentTime { set { NotifyFieldChanged(value); } get { return (double)NotifyGetFieldValue(double.NaN); } }
            public double Duration { set { NotifyFieldChanged(value); } get { return (double)NotifyGetFieldValue(double.NaN); } }
            public int IsPause { set { NotifyFieldChanged(value); } get { return (int)NotifyGetFieldValue(1); } }
            public int IsMute { set { NotifyFieldChanged(value); } get { return (int)NotifyGetFieldValue(1); } }
            public int Heartbeat { set { NotifyFieldChanged(value); } get { return (int)NotifyGetFieldValue(0); } }
            public int Flag { set { NotifyFieldChanged(value); } get { return (int)NotifyGetFieldValue(0); } }
            public int Length { set { NotifyFieldChanged(value); } get { return (int)NotifyGetFieldValue(0); } }
            [SharedMemeory(Size = 1024)]
            public byte[] Info { set { NotifyFieldChanged(value); } get { return (byte[])NotifyGetFieldValue(null); } }

        }
        static void Main(string[] args)
        {
            Console.WriteLine("父进程启动");
            //创建共享内存
            SharedData data = new SharedData();
            var sharedMemory = SharedMemory.Create("testshared", data);
            //创建互斥锁
            Mutex mutex = new Mutex(false,"testsharedMutex");
            Console.WriteLine("父进程发送:hello word!");
            //加锁写入消息
            mutex.WaitOne();
            data.Info= Encoding.ASCII.GetBytes("hello word!");
            mutex.ReleaseMutex();
            //启动子进程并将互斥锁名称传入
            var process = new Process();
            process.StartInfo.FileName = "ctestshared.exe";
            process.StartInfo.Arguments = "testsharedMutex";
            process.Start();
            Thread.Sleep(2000);
            Console.WriteLine("父进程发送:exit");
            //加锁写入消息
            mutex.WaitOne();
            data.Info = Encoding.ASCII.GetBytes("exit\0");
            mutex.ReleaseMutex();
            //等待子进行退出
            process.WaitForExit();
            //销毁共享内存及互斥锁
            sharedMemory.Dispose();
            mutex.Dispose();
            Console.WriteLine("父进程退出");
        }
    }
}

2、子进程

#include <iostream>
#include<Windows.h>
typedef struct _Shared {
	double CurentTime;//当前时间
	double Duration;//总时长
	int IsPause;
	int IsMute;
	int Heartbeat;//心跳
	int Flag;
	int Length;
	char Info[1024];
}Shared;
int main(int argc, char** argv)
{
	if (argc < 2)
		return -1;
	printf("子进程启动\n");
	//创建父进程的互斥锁
	auto mtx = CreateMutexA(NULL, FALSE, argv[1]);
	//打开共享内存
	HANDLE hfile = OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, "testshared");
	if (NULL == hfile)
	{
		MessageBoxA(0, "不能打开共享内存", "提示", MB_OK);
		return -1;
	}
	//映射地址
	Shared* shared;
	shared = (Shared*)MapViewOfFile(hfile, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(Shared));
	//进入互斥锁并读取数据
	WaitForSingleObject(mtx, INFINITE);
	printf("子进程接收:%s\n", shared->Info);
	ReleaseMutex(mtx);
	while (1)
	{
		Sleep(30);
		//进入互斥锁并读取数据
		WaitForSingleObject(mtx, INFINITE);
		if (strcmp(shared->Info, "exit")==0)
		{
			printf("子进程接收:%s\n", shared->Info);
			ReleaseMutex(mtx);
			break;
		}
		ReleaseMutex(mtx);
	}
	//销毁资源
	CloseHandle(hfile);
	CloseHandle(mtx);
	printf("子进程退出\n");
	return 0;
}

五、完整代码

C#使用共享内存实现进程间的通信-OS文档类资源-CSDN下载

### 回答1: c是拉丁字母表中的第三个字母,也是英语字母表中的一个字母。c的发音是/k/,是一个辅音字母。在英语中,c通常与元音字母a、o、u结合时发音为/k/,例如cat(猫)、car(车)、cup(杯子)。而与元音字母e、i、y结合时,通常发音为/s/,例如ceiling(天花板)、circle(圆圈)、cycle(循环)。此外,c还有一个特殊的发音,即与字母h结合时发音为/tʃ/,例如chocolate(巧克力)、church(教堂)。c在汉语拼音中常被用作辅音字母,发音为/pʰ/,例如ci(词)、ca(擦)。在化学元素周期表中,C是碳元素的符号。此外,C还有许多其他的含义和用法,比如在数学中表示圆的周长,表示温度的单位摄氏度等。总的来说,c是一个常用的字母,具有多种发音和用法,广泛应用于语言、科学和数学等领域。 ### 回答2: c是包含26个字母的拉丁字母表中的第三个字母。它是一个常见的字母,在英语中常被使用。它有时会用作形容词的首字母,如"C级"或"C类",表示某物的质量或能力较低。此外,c还用于表示温度的单位摄氏度。在数学中,c经常用作表示一个常数的符号,特别是当我们谈论到二次函数时。 在计算机科学中,c是一种编程语言的名称,它是一种非常古老而广泛使用的编程语言。它被广泛用于系统编程和开发应用程序。具有c编程经验的人员通常在计算机科学领域具有很高的就业竞争力。 此外,c也可以代表一些其他概念,如电容(电学中一个重要的概念),货币单位(如美元中的分),音乐中的一个音符(do),或者表示照相机的光圈值(光学术语)。 总而言之,c是一个常见的字母,拥有多种不同的用途和含义,无论是在语言、科学、技术还是其他领域中。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CodeOfCC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值