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

2 回答 2

0

编辑

下面的原始答案解决了过度紧密耦合的代码构造问题。但是,考虑到整个解决方案,我会避免只使用一个大缓冲区并以这种方式移交它的切片。您将代码暴露给缓冲区溢出(我们应该将其称为缓冲区“欠载”问题)。相反,我将管理一个字节数组数组,每个字节数组都是一个离散缓冲区。移交的偏移量始终为 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;
}

这要求您在使用代码的情况下调用 EventArgs 上的 SetBuffer SocketAsyncEventArgs。如果您担心这种方法会降低释放缓冲区并将其从套接字使用中删除的原子性,那么将这个调整后的缓冲区管理器子类化并SocketAsyncEventArgs在子类中包含特定代码。

于 2009-09-10T08:18:53.310 回答
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;
        }
    }
}
于 2009-09-11T09:37:26.883 回答