共享記憶體是高效能進程間通訊 (IPC) 方式之一。本文在基本 MemoryMappedFile 概念上改良,引入資料資訊區記錄目前資料長度與筆數,並支援指標運算實現任意位置讀寫,解決一般範例只能從頭寫入的限制。
length|count;後段為實際資料區。length 從資料區末尾順序追加,寫入後更新資訊區。length 位元組。Semaphore 保護資訊區讀寫,資料區讀寫若需並發可再加鎖。public class SharedMemoryManager : IDisposable
{
private const int InfoSize = 64; // 資訊區大小 (Unicode 字串 "length|count")
private MemoryMappedFile _mmf;
private MemoryMappedViewAccessor _accessor;
private Semaphore _semWriteInfo = new Semaphore(1, 1);
private bool _initialized;
private long _dataOffset; // 資料區起始位移
private long _capacity; // 資料區總容量
public bool Initialize(string mapName, long totalSize)
{
try
{
_mmf = MemoryMappedFile.CreateOrOpen(mapName, totalSize, MemoryMappedFileAccess.ReadWrite);
_accessor = _mmf.CreateViewAccessor();
_dataOffset = InfoSize;
_capacity = totalSize - InfoSize;
_initialized = true;
return true;
}
catch { return false; }
}
// 將 IntPtr 視為指標進行位移運算 (需 unsafe context 或 Marshal 操作)
private IntPtr GetWritePointer(int currentLength)
{
// 實際寫入位置 = 基底位址 + 資訊區大小 + 目前資料長度
return new IntPtr(_accessor.SafeMemoryMappedViewHandle.DangerousGetHandle().ToInt64() + _dataOffset + currentLength);
}
}
public bool ReadInfo(out int length, out int count)
{
length = count = 0;
if (!_initialized) return false;
byte[] buffer = new byte[InfoSize];
_accessor.ReadArray(0, buffer, 0, InfoSize);
string info = Encoding.Unicode.GetString(buffer).Trim('\0');
string[] parts = info.Split('|');
if (parts.Length == 2 && int.TryParse(parts[0], out length) && int.TryParse(parts[1], out count))
return true;
return false;
}
public bool WriteInfo(int length, int count)
{
if (!_initialized) return false;
_semWriteInfo.WaitOne();
try
{
string info = $"{length}|{count}";
byte[] buffer = Encoding.Unicode.GetBytes(info);
if (buffer.Length > InfoSize) return false;
_accessor.WriteArray(0, buffer, 0, buffer.Length);
// 清除殘留字元
if (buffer.Length < InfoSize)
_accessor.WriteArray(buffer.Length, new byte[InfoSize - buffer.Length], 0, InfoSize - buffer.Length);
return true;
}
finally { _semWriteInfo.Release(); }
}
public bool WriteData(byte[] data)
{
if (!_initialized || data.Length > _capacity) return false;
int curLen, curCount;
if (!ReadInfo(out curLen, out curCount)) return false;
if (curLen + data.Length > _capacity) return false; // 空間不足
// 寫入資料區
_accessor.WriteArray(_dataOffset + curLen, data, 0, data.Length);
// 更新資訊區
return WriteInfo(curLen + data.Length, curCount + 1);
}
public byte[] ReadAllData()
{
int len, cnt;
if (!ReadInfo(out len, out cnt) || len == 0) return new byte[0];
byte[] buffer = new byte[len];
_accessor.ReadArray(_dataOffset, buffer, 0, len);
return buffer;
}
offset|length,支援隨機存取特定筆資料。Mutex 或 EventWaitHandle 實現生產者/消費者通知機制。Global\\ 前缀 (需高權限) 或 Local\\ 區分會話。[StructLayout(LayoutKind.Sequential, Pack = 1)] 確保跨語言相容。