Предложения по управлению потокобезопасным неблокирующим буфером - PullRequest
1 голос
/ 10 сентября 2009

Я создал простой класс диспетчера буфера для использования с асинхронными сокетами. Это защитит от фрагментации памяти и улучшит производительность. Любые предложения по дальнейшим улучшениям или другие подходы?

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

Ответы [ 2 ]

0 голосов
/ 11 сентября 2009

Я создал новый класс с совершенно другим подходом.

У меня есть класс сервера, который получает байтовые массивы. Затем он будет вызывать различные делегаты, передавая им объекты буфера, чтобы другие классы могли обрабатывать их. Когда эти классы завершены, им нужен способ вернуть буферы обратно в стек.

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;
        }
    }
}
0 голосов
/ 10 сентября 2009

Редактировать

Оригинальный ответ ниже посвящен проблеме создания кода из-за чрезмерной жесткой связи. Тем не менее, рассматривая решение в целом, я бы не использовал только один большой буфер и не передавал его части таким образом. Вы подвергаете свой код переполнению буфера (и мы будем называть это проблемами "опустошения" буфера). Вместо этого я бы управлял массивом байтовых массивов, каждый из которых представляет собой отдельный буфер. Передаваемое смещение всегда равно 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));

Я не уверен, что вывод типов достаточно умен, чтобы разрешить типы buf, offset, size в этом случае. Если нет, вам придется поместить типы в список аргументов: -

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

Однако теперь ваш класс можно использовать для выделения буфера для всех требований, которые также используют шаблон byte [], int, int, который очень распространен.

Конечно, вам нужно отделить бесплатную операцию, но вот: -

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

Это требует от вас вызова SetBuffer для EventArgs при использовании кода в случае SocketAsyncEventArgs. Если вы обеспокоены тем, что этот подход уменьшает атомарность освобождения буфера и удаления его из использования сокетов, то подкласс этого настроенного менеджера буфера подкласс и включает SocketAsyncEventArgs специальный код в подкласс.

...