C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转
原文:C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转
节点通信存在两种模型:共享内存(Shared memory)和消息传递(Messages passing)。
内存映射文件对于托管世界的开发人员来说似乎很陌生,但它确实已经是很远古的技术了,而且在操作系统中地位相当。实际上,任何想要共享数据的通信模型都会在幕后使用它。
内存映射文件究竟是个什么?内存映射文件允许你保留一块地址空间,然后将该物理存储映射到这块内存空间中进行操作。物理存储是文件管理,而内存映射文件是操作系统级内存管理。


优势:
1.访问磁盘文件上的数据不需执行I/O操作和缓存操作(当访问文件数据时,作用尤其显著);
2.让运行在同一台机器上的多个进程共享数据(单机多进程间数据通信效率最高);
利用文件与内存空间之间的映射,应用程序(包括多个进程)可以通过直接在内存中进行读写来修改文件。.NET Framework 4 用托管代码按照本机Windows函数访问内存映射文件的方式来访问内存映射文件,管理 Win32 中的内存映射文件 。
有两种类型的内存映射文件:
持久内存映射文件
持久文件是与磁盘上的源文件关联的内存映射文件。在最后一个进程使用完此文件后,数据将保存到磁盘上的源文件中。这些内存映射文件适合用来处理非常大的源文件。
非持久内存映射文件
非持久文件是未与磁盘上的源文件关联的内存映射文件。当最后一个进程使用完此文件后,数据将丢失,并且垃圾回收功能将回收此文件。这些文件适用于为进程间通信 (IPC) 创建共享内存。
1)在多个进程之间进行共享(进程可通过使用由创建同一内存映射文件的进程所指派的公用名来映射到此文件)。
2)若要使用一个内存映射文件,则必须创建该内存映射文件的完整视图或部分视图。还可以创建内存映射文件的同一部分的多个视图,进而创建并发内存。为了使两个视图能够并发,必须基于同一内存映射文件创建这两个视图。
3)如果文件大于应用程序用于内存映射的逻辑内存空间(在 32 位计算机上为2GB),则还需要使用多个视图。
有两种类型的视图:流访问视图和随机访问视图。使用流访问视图可对文件进行顺序访问;在使用持久文件时,随机访问视图是首选方法。
.Net 共享内存 内存映射文件原理:通过操作系统的内存管理器访问的,因此会自动将此文件分隔为多个页,并根据需要对其进行访问。您不需要自行处理内存管理。如下图:

C# .Net 共享内存 演示代码如下:
//持久内存映射文件:基于现有文件创建一个具有指定公用名的内存映射文件
using (var mmf = MemoryMappedFile.CreateFromFile(@"c:\内存映射文件.data", FileMode.Open, "公用名"))
{
//通过指定的 偏移量和大小 创建内存映射文件视图服务器
using (var accessor = mmf.CreateViewAccessor(offset, length)) //偏移量,可以控制数据存储的内存位置;大小,用来控制存储所占用的空间
{
//Marshal提供了一个方法集,这些方法用于分配非托管内存、复制非托管内存块、将托管类型转换为非托管类型,此外还提供了在与非托管代码交互时使用的其他杂项方法。
int size = Marshal.SizeOf(typeof(char));
//修改内存映射文件视图
for (long i = 0; i < length; i += size)
{
char c= accessor.ReadChar(i);
accessor.Write(i, ref c);
}
}
}
//另一个进程或线程可以,在系统内存中打开一个具有指定名称的现有内存映射文件
using (var mmf = MemoryMappedFile.OpenExisting("公用名"))
{
using (var accessor = mmf.CreateViewAccessor(4000000, 2000000))
{
int size = Marshal.SizeOf(typeof(char));
for (long i = 0; i < length; i += size)
{
char c = accessor.ReadChar(i);
accessor.Write(i, ref c);
}
}
}
//非持久内存映射文件:未映射到磁盘上的现有文件的内存映射文件
using (MemoryMappedFile mmf = MemoryMappedFile.CreateNew("testmap", 10000))
{
bool mutexCreated;
//进程间同步
Mutex mutex = newMutex(true, "testmapmutex", out mutexCreated);
using (var stream = mmf.CreateViewStream()) //创建文件内存视图流 基于流的操作
{
var writer = newBinaryWriter(stream);
writer.Write(1);
}
mutex.ReleaseMutex();
Console.WriteLine("Start Process B and press ENTER to continue.");
Console.ReadLine();
mutex.WaitOne();
using (MemoryMappedViewStream stream = mmf.CreateViewStream())
{
var reader = newBinaryReader(stream);
Console.WriteLine("Process A says: {0}", reader.ReadBoolean());
Console.WriteLine("Process B says: {0}", reader.ReadBoolean());
}
mutex.ReleaseMutex();
}
using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting("testmap"))
{
Mutex mutex = Mutex.OpenExisting("testmapmutex");
mutex.WaitOne();
using (var stream = mmf.CreateViewStream(1, 0))//注意这里的偏移量
{
var writer = newBinaryWriter(stream);
writer.Write(0);
}
mutex.ReleaseMutex();
}
C# .Net 进程间通信 共享内存 完整示例: C#共享内存非持久化方式通讯的例子,通讯时的线程和进程控制也没有问题。如下是实现的代码。
先启动消息服务IMServer_Message,
再启动状态服务IMServer_State,
IMServer_Message回车一次(创建共享内存公用名和公用线程锁,并视图流方式写共享内存),
IMServer_State回车一次(获取共享内存并视图流方式写、视图访问器写入结构体类型)
并立刻IMServer_Message再回车一次(读取刚刚写入的信息),
观察IMServer_State屏显变化并等待(线程锁)约5s(线程锁被释放)后
在IMServer_Message上观察屏显(显示刚刚写入共享内存的信息)
IMServer_Message.exe 代码
using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;
using System.Threading;
namespace IMServer_Message
{
/// <summary>
/// 用于共享内存方式通信的 值类型 结构体
/// </summary>
public struct ServiceMsg
{
public int Id;
public long NowTime;
}
internal class Program
{
private static void Main(string[] args)
{
Console.Write("请输入共享内存公用名(默认:testmap):");
string shareName = Console.ReadLine();
if (string.IsNullOrEmpty(shareName))
shareName = "testmap";
using (MemoryMappedFile mmf = MemoryMappedFile.CreateOrOpen(shareName, 1024000,MemoryMappedFileAccess.ReadWrite))
{
bool mutexCreated;
//进程间同步
var mutex = new Mutex(true, "testmapmutex", out mutexCreated);
using (MemoryMappedViewStream stream = mmf.CreateViewStream()) //创建文件内存视图流
{
var writer = new BinaryWriter(stream);
for (int i = 0; i < 5; i++)
{
writer.Write(i);
Console.WriteLine("{0}位置写入流:{0}", i);
}
}
mutex.ReleaseMutex();
Console.WriteLine("启动状态服务,按【回车】读取共享内存数据");
Console.ReadLine();
mutex.WaitOne();
using (MemoryMappedViewStream stream = mmf.CreateViewStream())
{
var reader = new BinaryReader(stream);
for (int i = 0; i < 10; i++)
{
Console.WriteLine("{1}位置:{0}", reader.ReadInt32(), i);
}
}
using (MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor(1024, 10240))
{
int colorSize = Marshal.SizeOf(typeof (ServiceMsg));
ServiceMsg color;
for (int i = 0; i < 50; i += colorSize)
{
accessor.Read(i, out color);
Console.WriteLine("{1}\tNowTime:{0}", new DateTime(color.NowTime), color.Id);
}
}
mutex.ReleaseMutex();
}
Console.WriteLine("测试: 我是 即时通讯 - 消息服务 我启动啦!!!");
Console.ReadKey();
}
}
}
IMServer_State.exe代码
using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;
using System.Threading;
namespace IMServer_State
{
/// <summary>
/// 用于共享内存方式通信的 值类型 结构体
/// </summary>
public struct ServiceMsg
{
public int Id;
public long NowTime;
}
internal class Program
{
private static void Main(string[] args)
{
Console.Write("请输入共享内存公用名(默认:testmap):");
string shareName = Console.ReadLine();
if (string.IsNullOrEmpty(shareName))
shareName = "testmap";
using (MemoryMappedFile mmf = MemoryMappedFile.CreateOrOpen(shareName, 1024000,MemoryMappedFileAccess.ReadWrite))
{
Mutex mutex = Mutex.OpenExisting("testmapmutex");
mutex.WaitOne();
using (MemoryMappedViewStream stream = mmf.CreateViewStream(20, 0)) //注意这里的偏移量
{
var writer = new BinaryWriter(stream);
for (int i = 5; i < 10; i++)
{
writer.Write(i);
Console.WriteLine("{0}位置写入流:{0}", i);
}
}
using (MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor(1024, 10240))
{
int colorSize = Marshal.SizeOf(typeof (ServiceMsg));
var color = new ServiceMsg();
for (int i = 0; i < colorSize*5; i += colorSize)
{
color.Id = i;
color.NowTime = DateTime.Now.Ticks;
//accessor.Read(i, out color);
accessor.Write(i, ref color);
Console.WriteLine("{1}\tNowTime:{0}", new DateTime(color.NowTime), color.Id);
Thread.Sleep(1000);
}
}
Thread.Sleep(5000);
mutex.ReleaseMutex();
}
Console.WriteLine("测试: 我是 即时通讯 - 状态服务 我启动啦!!!");
Console.ReadKey();
}
}
}
C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转的更多相关文章
- C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转 VC中进程与进程之间共享内存 .net环境下跨进程、高频率读写数据 使用C#开发Android应用之WebApp 分布式事务之消息补偿解决方案
C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转 节点通信存在两种模型:共享内存(Shared memory)和消息传递(Messages passing). ...
- C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped
节点通信存在两种模型:共享内存(Shared memory)和消息传递(Messages passing). 内存映射文件对于托管世界的开发人员来说似乎很陌生,但它确实已经是很远古的技术了,而且在操作 ...
- JAVA NIO之浅谈内存映射文件原理与DirectMemory
JAVA类库中的NIO包相对于IO 包来说有一个新功能是内存映射文件,日常编程中并不是经常用到,但是在处理大文件时是比较理想的提高效率的手段.本文我主要想结合操作系统中(OS)相关方面的知识介绍一下原 ...
- 【NIO】NIO之浅谈内存映射文件原理与DirectMemory
Java类库中的NIO包相对于IO 包来说有一个新功能是内存映射文件,日常编程中并不是经常用到,但是在处理大文件时是比较理想的提高效率的手段.本文我主要想结合操作系统中(OS)相关方面的知识介绍一下原 ...
- Linux下多任务间通信和同步-mmap共享内存
Linux下多任务间通信和同步-mmap共享内存 嵌入式开发交流群280352802,欢迎加入! 1.简介 共享内存可以说是最有用的进程间通信方式.两个不用的进程共享内存的意思是:同一块物理内存被映射 ...
- Windows进程通信 -- 共享内存(1)
共享内存的方式原理就是将一份物理内存映射到不同进程各自的虚拟地址空间上,这样每个进程都可以读取同一份数据,从而实现进程通信.因为是通过内存操作实现通信,因此是一种最高效的数据交换方法. 共享内存在 W ...
- Windows进程通信 -- 共享内存
享内存的方式原理就是将一份物理内存映射到不同进程各自的虚拟地址空间上,这样每个进程都可以读取同一份数据,从而实现进程通信.因为是通过内存操作实现通信,因此是一种最高效的数据交换方法. 共享内存在 Wi ...
- Linux学习笔记(14)-进程通信|共享内存
在Linux中,共享内存是允许两个不相关的进程访问同一个逻辑内存的进程间通信方法,是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式. 不同进程之间共享的内存通常安排为同一段物理内存.进程可 ...
- mmap映射文件至内存( 实现 共享内存 与 文件的另类访问 )
Linux提供了内存映射函数mmap, 它把文件内容映射到一段内存上(准确说是虚拟内存上), 通过对这段内存的读取和修改, 实现对文件的读取和修改, 先来看一下mmap的函数声明: 头文件: < ...
随机推荐
- MFC单文档自定义扩展名及添加图标报Assertion错误
忽然无聊的想给自己写的程序保存的文件使用自己的名字简写作为后缀,于是有了下文. IDR_MAINFRAME格式介绍 IDR_MAINFRAME字符串资源中包含7个子串,分别以/n结束,即如下格式: & ...
- sql Sever 修改表中的列名
EXEC SP_RENAME 'LeInterface.iYear','iLeYear','COLUMN' EXEC SP_RENAME 'LeInterface.imonth','iLemonth' ...
- Emacs快捷键列表
C = ControlM = Meta = Alt | EscDel = Backspace 基本快捷键(Basic)C-x C-f "find"文件, 即在缓冲区打开/新建一个文 ...
- 基于 canvas 将图片转化成字符画
字符画大家一定非常熟悉了,那么如何把一张现有的图片转成字符画呢? HTML5 让这个可能变成了现实,通过 canvas,可以很轻松实现这个功能. 其实原理很简单:扫描图片相应位置的像素点,再计算出其灰 ...
- python 学习资料
Python是一种面向对象.直译式计算机程序设计语言.它的语法简捷和清晰,尽量使用无异义的英语单词,与其它大多数程序设计语言使用大括号不一样,它使用縮进来定义语句块.与Scheme.Ruby.Perl ...
- zookeeper[6] zookeeper FAQ(转)
转自:http://www.cnblogs.com/zhengran/p/4601855.html 1. 如何处理CONNECTION_LOSS?在Zookeeper中,服务器和客户端之间维持一个长连 ...
- 【转】windows下vs2008/2010+opencv2.2开发环境搭建
版权声明:本文为博主原创文章,未经博主允许不得转载. 1.下载安装Cmake 2.用cmake配置opencv2.2,然后编译,安装 3. 在vs2008中配置opencv2.2 4.Demo 1.下 ...
- 解决Wamp 开启vhost localhost 提示 403 Forbbiden 的问题!
非常奇怪的一个问题.我曾经从来都没有这样过!訪问 http://localhost/ 提示 403 Forbbiden. 我之前的设置一直都是这种: httpd.conf <Directory ...
- Swift学习——A Swift Tour 函数
Functions and Closures 函数和封闭性(闭包) Functions 函数的使用 Swift中的函数定义和OC中有明显的差别了,使用func定义函数,在括号里定义參数和类型,用 ...
- 邮件协议(SMTP)性能测试总结(Foxmail邮箱)
先介绍一下邮件协议SMTP的工作机制(连接和发送过程),用wireshark工具抓包进行分析,如下: SMTP协议的工作机制(连接和发送过程): 1.建立TCP连接,并将邮件服务器地址给客户端: 2. ...