節點通訊存在兩種模型:共享內存(Shared memory)和消息傳遞(Messages passing)。html
內存映射文件對於託管世界的開發人員來講彷佛很陌生,但它確實已是很遠古的技術了,並且在操做系統中地位至關。實際上,任何想要共享數據的通訊模型都會在幕後使用它。緩存
內存映射文件到底是個什麼?內存映射文件容許你保留一塊地址空間,而後將該物理存儲映射到這塊內存空間中進行操做。物理存儲是文件管理,而內存映射文件是操做系統級內存管理。服務器
優點:
1.訪問磁盤文件上的數據不需執行I/O操做和緩存操做(當訪問文件數據時,做用尤爲顯著);
2.讓運行在同一臺機器上的多個進程共享數據(單機多進程間數據通訊效率最高);併發
利用文件與內存空間之間的映射,應用程序(包括多個進程)能夠經過直接在內存中進行讀寫來修改文件。.NET Framework 4 用託管代碼按照本機Windows函數訪問內存映射文件的方式來訪問內存映射文件,管理 Win32 中的內存映射文件 。app
有兩種類型的內存映射文件:函數
持久內存映射文件post
持久文件是與磁盤上的源文件關聯的內存映射文件。在最後一個進程使用完此文件後,數據將保存到磁盤上的源文件中。這些內存映射文件適合用來處理很是大的源文件。測試
非持久內存映射文件spa
非持久文件是未與磁盤上的源文件關聯的內存映射文件。當最後一個進程使用完此文件後,數據將丟失,而且垃圾回收功能將回收此文件。這些文件適用於爲進程間通訊 (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(); } }}