首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 共享記憶體 進階實作:支援順序讀寫與資料資訊區

     編輯:關於C#

共享記憶體是高效能進程間通訊 (IPC) 方式之一。本文在基本 MemoryMappedFile 概念上改良,引入資料資訊區記錄目前資料長度與筆數,並支援指標運算實現任意位置讀寫,解決一般範例只能從頭寫入的限制。

核心設計概念

  1. 記憶體配置:前段保留固定大小的「資料資訊區」 (Info Size),儲存 length|count;後段為實際資料區。
  2. 寫入策略:根據目前 length 從資料區末尾順序追加,寫入後更新資訊區。
  3. 讀取策略:一次讀取資料區前 length 位元組。
  4. 同步機制:使用 Semaphore 保護資訊區讀寫,資料區讀寫若需並發可再加鎖。

關鍵程式碼片段

1. 初始化與指標計算

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);
    }
}

2. 讀寫資訊區

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(); }
}

3. 寫入資料 (附加模式)

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);
}

4. 讀取所有資料

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\\ 區分會話。
  • 32 位元程式總記憶體限制 2GB/4GB,大容量共享記憶體請改用 64 位元程式。
  • 結構體對齊問題:使用 [StructLayout(LayoutKind.Sequential, Pack = 1)] 確保跨語言相容。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved