Sugestões para um gerenciador de buffer segmento seguro sem bloqueio
-
05-07-2019 - |
Pergunta
Eu criei uma classe gerenciador de buffer simples de ser usado com soquetes asyncroneous. Isto irá proteger contra a fragmentação da memória e melhorar o desempenho. Todas as sugestões para melhorias ou outras abordagens?
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;
}
}
Solução
Editar :
A resposta orignal abaixo aborda um problema de construção código de acoplamento excessivamente apertado. No entanto, considerando a solução como toda a gostaria de evitar o uso de apenas um grande buffer e entrega fatias de la desta forma. Você expor o seu código para saturação de buffer (e vamos chamá-lo de tampão "contra o encaixe" questões). Em vez disso eu gerir um conjunto de matrizes de bytes sendo cada um tampão discreta. Deslocamento entregue é sempre 0 e tamanho é sempre o comprimento do tampão. Qualquer código ruim que tentativas de ler / escrever peças para além das fronteiras será pego.
resposta Original
Você acoplada a classe para SocketAsyncEventArgs onde de fato tudo o que precisa é uma função para atribuir o buffer, a mudança SetBuffer a: -
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);
}
Agora você pode chamar de consumir código algo como isto: -
myMgr.SetBuffer((buf, offset, size) => myArgs.SetBuffer(buf, offset, size));
Eu não tenho certeza de que tipo de inferência é bastante inteligente para resolver os tipos de buf, offset, size
neste caso. Se não você terá que colocar os tipos na lista de argumentos: -
myMgr.SetBuffer((byte[] buf, int offset, int size) => myArgs.SetBuffer(buf, offset, size));
No entanto, agora a sua classe pode ser usada para alocar um buffer para todos os tipos de requisitos que também usam o byte [], int, int padrão que é muito comum.
Claro que você precisa para desacoplar a operação livre para, mas isso é: -
public void FreeBuffer(byte[] buff, int offset)
{
if (buffer == buff)
free[offset / blocksize] = 1;
}
Isso requer que você chamar SetBuffer nas EventArgs em consumir código no caso de SocketAsyncEventArgs
. Se você está preocupado que esta abordagem reduz a atomicidade de libertar o buffer e retirando-o das tomadas de usar, em seguida, sub-classe este gerente e tampão ajustado incluir código específico SocketAsyncEventArgs
na sub-classe.
Outras dicas
Eu criei uma nova classe com uma abordagem completamente diferente.
Eu tenho uma classe servidor que recebe matrizes de bytes. Ele vai então invocar diferentes delegados entregando-o tampão de objectos de modo a que outras classes podem processá-los. Quando essas classes são feitos eles precisam de uma maneira de empurrar os buffers de volta à pilha.
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;
}
}
}