对线程安全非阻塞缓冲区管理器的建议

时间:2009-09-10 08:05:17

标签: c# multithreading buffer nonblocking

我创建了一个简单的缓冲区管理器类,用于异步套接字。这将防止内存碎片并提高性能。有关进一步改进或其他方法的建议吗?

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 :(得分:0)

修改

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

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

答案 1 :(得分: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;
        }
    }
}