2009-09-10 75 views
1

我創建了一個簡單的緩衝區管理器類,用於異步套接字。這將防止內存碎片並提高性能。任何關於進一步改進或其他方法的建議?對線程安全無阻塞緩衝區管理器的建議

public class BufferManager 
{ 
    private int[] free; 
    private byte[] buffer; 
    private readonly int blocksize; 

    public BufferManager(int count, int blocksize) 
    { 
     buffer = new byte[count * blocksize]; 
     free = new int[count]; 
     this.blocksize = blocksize; 

     for (int i = 0; i < count; i++) 
      free[i] = 1; 
    } 

    public void SetBuffer(SocketAsyncEventArgs args) 
    { 
     for (int i = 0; i < free.Length; i++) 
     { 
      if (1 == Interlocked.CompareExchange(ref free[i], 0, 1)) 
      { 
       args.SetBuffer(buffer, i * blocksize, blocksize); 
       return; 
      } 
     } 
     args.SetBuffer(new byte[blocksize], 0, blocksize); 
    } 

    public void FreeBuffer(SocketAsyncEventArgs args) 
    { 
     int offset = args.Offset; 
     byte[] buff = args.Buffer; 

     args.SetBuffer(null, 0, 0); 

     if (buffer == buff) 
      free[offset/blocksize] = 1; 
    } 
} 

回答

0

編輯

下一部開拓創新的答案解決過於緊密耦合的代碼結構問題。但是,考慮到整個解決方案,我會避免只使用一個大緩衝區,並以這種方式移交它的片段。你將你的代碼暴露在緩衝區溢出(我們應該把它稱爲緩衝區「underrun」問題)。相反,我會管理一個字節數組,每個數組都是一個離散的緩衝區。移交的偏移總是0,大小始終是緩衝區的長度。任何試圖讀取/寫入超出邊界的錯誤代碼都會被捕獲。

原來的答覆

你加上的類的SocketAsyncEventArgs那裏其實它所需要的是分配緩衝區的函數,改變SetBuffer到: -

public void SetBuffer(Action<byte[], int, int> fnSet) 
{ 
    for (int i = 0; i < free.Length; i++) 
    { 
     if (1 == Interlocked.CompareExchange(ref free[i], 0, 1)) 
     { 
      fnSet(buffer, i * blocksize, blocksize); 
      return; 
     } 
    } 
    fnSet(new byte[blocksize], 0, blocksize); 
} 

現在你可以調用從消費類似這樣的代碼: -

myMgr.SetBuffer((buf, offset, size) => myArgs.SetBuffer(buf, offset, size)); 

我不確定該類型推理是否足夠聰明resolv e這種情況下的buf, offset, size的類型。如果沒有,你將不得不放置類型參數列表: -

myMgr.SetBuffer((byte[] buf, int offset, int size) => myArgs.SetBuffer(buf, offset, size)); 

但是現在你的類可用於分配緩衝區的要求,所有的方式也使用字節[],INT,INT模式這很常見。

當然,你需要去耦自由操作但那是: -

public void FreeBuffer(byte[] buff, int offset) 
{ 
    if (buffer == buff) 
     free[offset/blocksize] = 1; 
} 

這需要你在消費的情況下,代碼SocketAsyncEventArgs調用SetBuffer上的EventArgs。如果您擔心這種方法會減少釋放緩衝區並將其從套接字中刪除的原子性,那麼將此調整後的緩衝區管理器子類化,並在子類中包含特定的代碼。

+0

偉大的建議!首先,我決定使用一個larg緩衝區來防止內存碎片。但是,當所有字節將被同時分配時,可能無關緊要。 – remdao

+0

由於只有80K或以上的分配才從沒有壓縮的大對象堆獲取,所以碎片只會成爲大於80K左右的緩衝區的問題。任何少數東西都來自GC在收集之後會緊湊的正常堆,從而消除任何碎片。 – AnthonyWJones

+0

我意識到,將緩衝區管理器實現爲一個字節數組堆棧可能會更好。因爲如果它不需要跟蹤偏移量,那麼沒有理由產生循環。推送和彈出字節數組對象會更快。 – remdao

0

我用完全不同的方法創建了一個新類。

我有一個接收字節數組的服務器類。然後它會調用不同的委託給它們緩衝對象,以便其他類可以處理它們。當這些類完成時,他們需要一種將緩衝區推回堆棧的方法。

public class SafeBuffer 
{ 
    private static Stack bufferStack; 
    private static byte[][] buffers; 

    private byte[] buffer; 
    private int offset, lenght; 

    private SafeBuffer(byte[] buffer) 
    { 
     this.buffer = buffer; 
     offset = 0; 
     lenght = buffer.Length; 
    } 

    public static void Init(int count, int blocksize) 
    { 
     bufferStack = Stack.Synchronized(new Stack()); 
     buffers = new byte[count][]; 

     for (int i = 0; i < buffers.Length; i++) 
      buffers[i] = new byte[blocksize]; 

     for (int i = 0; i < buffers.Length; i++) 
      bufferStack.Push(new SafeBuffer(buffers[i])); 
    } 

    public static SafeBuffer Get() 
    { 
     return (SafeBuffer)bufferStack.Pop(); 
    } 

    public void Close() 
    { 
     bufferStack.Push(this); 
    } 

    public byte[] Buffer 
    { 
     get 
     { 
      return buffer; 
     } 
    } 

    public int Offset 
    { 
     get 
     { 
      return offset; 
     } 
     set 
     { 
      offset = value; 
     } 
    } 

    public int Lenght 
    { 
     get 
     { 
      return buffer.Length; 
     } 
    } 
}