節點通訊存在兩種模型:共用記憶體(Shared memory)和訊息傳遞(Messages passing)。
記憶體對應檔對於託管世界的開發人員來說似乎很陌生,但它確實已經是很遠古的技術了,而且在作業系統中地位相當。實際上,任何想要共用資料的通訊模型都會在幕後使用它。
記憶體對應檔究竟是個什麼?記憶體對應檔允許你保留一塊地址空間,然後將該實體儲存體映射到這塊記憶體空間中進行操作。實體儲存體是檔案管理,而記憶體對應檔是作業系統級記憶體管理。
優勢:
1.訪問磁碟檔案上的資料不需執行I/O操作和快取作業(當訪問檔案資料時,作用尤其顯著);
2.讓運行在同一台機器上的多個進程共用資料(單機多進程間資料通訊效率最高);
利用檔案與記憶體空間之間的映射,應用程式(包括多個進程)可以通過直接在記憶體中進行讀寫來修改檔案。.NET Framework 4 用Managed 程式碼按照本機Windows函數訪問記憶體對應檔的方式來訪問記憶體對應檔,管理 Win32 中的記憶體對應檔 。
有兩種類型的記憶體對應檔:
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提供了一個方法集,這些方法用於分配非託管記憶體、複製非託管記憶體塊、將託管類型轉換為非託管類型,此外還提供了在與Unmanaged 程式碼互動時使用的其他雜項方法。
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#共用記憶體非持久化方式通訊的例子,通訊時的線程和進程式控制制也沒有問題。如下是實現的代碼。
先啟動Message ServiceIMServer_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("測試: 我是 即時通訊 - Message Service 我啟動啦!!!");
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();
}
}
}