Domanda

Ho creato una semplice classe di gestione buffer da utilizzare con socket asincroni. Ciò proteggerà dalla frammentazione della memoria e migliorerà le prestazioni. Qualche suggerimento per ulteriori miglioramenti o altri approcci?

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;
    }
}
È stato utile?

Soluzione

Modifica :

La risposta originale di seguito affronta un problema di costruzione del codice di accoppiamento eccessivamente stretto. Tuttavia, considerando la soluzione nel suo insieme, eviterei di utilizzare solo un buffer di grandi dimensioni e di distribuirne parti in questo modo. Esponi il tuo codice al buffer overrun (e dovremmo chiamarlo buffer "underrun" issue). Gestirei invece una matrice di array di byte, ciascuno dei quali è un buffer discreto. L'offset consegnato è sempre 0 e la dimensione è sempre la lunghezza del buffer. Qualsiasi codice errato che tenti di leggere / scrivere parti oltre i limiti verrà catturato.

Risposta originale

Hai abbinato la classe a SocketAsyncEventArgs dove in effetti tutto ciò che serve è una funzione per assegnare il buffer, cambiare SetBuffer in: -

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

Ora puoi chiamare consumando codice in questo modo: -

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

Non sono sicuro che l'inferenza del tipo sia abbastanza intelligente da risolvere i tipi di buf, offset, size in questo caso. Altrimenti dovrai inserire i tipi nell'elenco degli argomenti: -

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

Tuttavia ora la tua classe può essere utilizzata per allocare un buffer per tutti i tipi di requisiti che usano anche il modello byte [], int, int che è molto comune.

Ovviamente devi disaccoppiare l'operazione gratuita ma questo è: -

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

Ciò richiede di chiamare SetBuffer su EventArgs per utilizzare il codice nel caso di SocketAsyncEventArgs . Se si teme che questo approccio riduca l'atomicità di liberare il buffer e rimuoverlo dall'uso dei socket, allora classificare questo gestore buffer modificato e includere il codice specifico SocketAsyncEventArgs nella sottoclasse.

Altri suggerimenti

Ho creato una nuova classe con un approccio completamente diverso.

Ho una classe di server che riceve array di byte. Richiamerà quindi diversi delegati consegnando loro gli oggetti buffer in modo che altre classi possano elaborarli. Al termine di tali classi, hanno bisogno di un modo per riportare i buffer nello stack.

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;
        }
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top