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

網路資料處理緩衝區與緩衝池 C# 實作:高效能記憶體重用模式

     編輯:關於C

在高併發網路程式設計中,頻繁分配/釋放位元組陣列會造成嚴重的 GC 壓力與記憶體碎片。緩衝池模式透過預先分配固定大小的緩衝區並循環重用,顯著降低記憶體分配開銷。本文展示完整的 C# DataBuffer 與 BufferPool 實作,包含執行緒安全、IDisposable 模式、多池負載均衡等生產級特性。

DataBuffer:可重用的位元組緩衝區


public class DataBuffer : IDisposable
{
    public byte[] Data;           // 底層儲存陣列
    private int mLength;          // 緩衝區總容量
    private int mPosition = 0;    // 當前讀寫位置
    internal int mCount = 0;      // 目前有效資料長度
    
    internal BufferPool Pool { get; set; }  // 所屬池,用於 Dispose 時歸還

    // 以現有陣列建立 (資料已存在)
    public DataBuffer(byte[] data) {
        Data = data;
        mLength = data.Length;
        mPosition = 0;
        mCount = data.Length;
    }

    // 指定容量建立 (空緩衝區)
    public DataBuffer(int length) {
        mLength = length;
        Data = new byte[length];
    }

    // 從外部陣列複製資料
    public void From(Array source, int index, int count) {
        Array.Copy(source, index, Data, 0, count);
        mPosition = 0;
        mCount = count;
    }

    // 寫入資料,返回實際寫入位元組數
    public int Write(byte[] data) => Write(data, 0);
    
    public int Write(byte[] data, int index) {
        int available = mLength - mPosition;
        int request = data.Length - index;
        int count = Math.Min(available, request);
        
        if (count > 0) {
            Array.Copy(data, index, Data, mPosition, count);
            mPosition += count;
            mCount += count;
        }
        return count;
    }

    // 讀取資料,返回 ArraySegment 避免複製
    public ArraySegment<byte> Read(int count) {
        int end = count;
        if (mPosition + count > mCount)
            end = mCount - mPosition;
        
        var result = new ArraySegment<byte>(Data, mPosition, end);
        mPosition += end;
        return result;
    }

    // 重設位置
    public void Seek() => Seek(0);
    public void Seek(int position) => mPosition = position;

    // 取得有效資料區段
    public ArraySegment<byte> GetSegment() => new ArraySegment<byte>(Data, 0, mCount);

    // 歸還緩衝池
    public void Dispose() {
        if (Pool != null) {
            mPosition = 0;
            mCount = 0;
            Pool.Push(this);
        }
    }
}

設計亮點

  • ArraySegment 回傳:讀取不複製記憶體,支援零拷貝網路傳輸
  • 位置追蹤:mPosition 支援流式讀寫、隨機存取
  • 容量保護:Write 自動限制寫入量,防止緩衝區溢位
  • IDisposable 模式:using 區塊自動歸還池中,杜絕洩漏

BufferPool:執行緒安全的多池負載均衡


public class BufferPool : IDisposable
{
    private static List<BufferPool> mPools = new List<BufferPool>();
    private static int mIndex = 0;
    private readonly Queue<DataBuffer> mBuffers;
    private readonly int mBufferLength;
    private bool mDisposed = false;

    // 建構函數:預先分配指定數量緩衝區
    public BufferPool(int count, int bufferLength) {
        mBufferLength = bufferLength;
        mBuffers = new Queue<DataBuffer>(count);
        for (int i = 0; i < count; i++)
            mBuffers.Enqueue(CreateBuffer(bufferLength));
    }

    private DataBuffer CreateBuffer(int length) {
        var item = new DataBuffer(length);
        item.Pool = this;
        return item;
    }

    // 取得緩衝區:池空時自動擴容
    public DataBuffer Pop() {
        lock (mBuffers) {
            return mBuffers.Count > 0 ? mBuffers.Dequeue() : CreateBuffer(mBufferLength);
        }
    }

    // 歸還緩衝區
    public void Push(DataBuffer buffer) {
        lock (mBuffers) {
            mBuffers.Enqueue(buffer);
        }
    }

    // 靜態工廠:建立多個池以分散鎖競爭
    public static void Setup(int pools, int buffers) => Setup(pools, buffers, 2048);
    
    public static void Setup(int pools, int buffers, int bufferLength) {
        lock (mPools) {
            for (int i = 0; i < pools; i++)
                mPools.Add(new BufferPool(buffers, bufferLength));
        }
    }

    // 取得池:循環分發實現負載均衡
    public static BufferPool GetPool() {
        lock (mPools) {
            if (mIndex == mPools.Count) mIndex = 0;
            return mPools[mIndex++];
        }
    }

    // 清理所有池
    public static void Clean() {
        lock (mPools) {
            foreach (var pool in mPools) pool.Dispose();
            mPools.Clear();
        }
    }

    // 釋放池資源
    private void OnDispose() {
        lock (mBuffers) {
            while (mBuffers.Count > 0)
                mBuffers.Dequeue().Pool = null;  // 斷開關聯防止誤歸還
        }
    }

    public void Dispose() {
        lock (this) {
            if (!mDisposed) {
                OnDispose();
                mDisposed = true;
            }
        }
    }
}

多池架構優勢

問題單池多池 + 輪詢分發
鎖競爭高併發下單一 Queue 鎖熱點分散到多個獨立鎖
快取親和性跨核心存取同一 Queue執行緒傾向固定池,提升快取命中
擴容衝突同時擴容觸發大量分配分散擴容壓力

使用範例


// 應用啟動時初始化:4 個池,每池 100 個 4KB 緩衝區
BufferPool.Setup(4, 100, 4096);

// 網路接收迴圈
var pool = BufferPool.GetPool();
using (var buffer = pool.Pop()) {
    int received = socket.Receive(buffer.Data, 0, buffer.Data.Length, SocketFlags.None);
    if (received > 0) {
        buffer.mCount = received;
        ProcessData(buffer.GetSegment());
    }
} // 自動歸還池中

// 發送端同樣受益
using (var buffer = pool.Pop()) {
    buffer.From(messageBytes, 0, messageBytes.Length);
    socket.Send(buffer.GetSegment());
}

進階擴展方向

  1. BufferWriter/BufferReader:封裝跨多緩衝區的物件序列化/反序列化
  2. 大小分級池:依據訊息大小分配不同規格池 (如 1KB/4KB/16KB/64KB),減少內部碎片
  3. 記憶體對齊:使用 ArrayPool<byte>.Shared (`.NET Core 2.1+`) 替代自建池
  4. 效能計數器:監控池命中率、擴容頻率、平均佇列長度
  5. 非同步支援:配合 ValueTask 與 Memory<byte> 實作零拷貝非同步管線

總結

緩衝池是高效能網路程式的基石組件。DataBuffer + BufferPool 的組合提供了:

  • 確定性記憶體使用 (無非預期分配)
  • 極低延遲分配/釋放 (O(1) 佇列操作)
  • 執行緒安全與負載均衡
  • 零拷貝資料傳遞介面 (ArraySegment<byte>)

掌握此模式,是構建百萬併發伺服器、高頻交易系統、即時通訊網關的關鍵技能之一。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved