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

发布于 2024-08-04 12:42:26 字数 1080 浏览 6 评论 0原文

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

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

I've created a simple buffer manager class to be used with asyncroneous sockets. This will protect against memory fragmentation and improve performance. Any suggestions for further improvements or other approaches?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

无语# 2024-08-11 12:42:26

编辑

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

I'在这种情况下,我不确定类型推断是否足够聪明来解析 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 特定代码。

Edit:

The orignal answer below addresses a code construction issue of overly tight coupling. However, considering the solution as whole I would avoid using just one large buffer and handing over slices of it in this way. You expose your code to buffer overrun (and shall we call it buffer "underrun" issues). Instead I would manage an array of byte arrays each being a discrete buffer. Offset handed over is always 0 and size is always the length of the buffer. Any bad code that attempts to read/write parts beyond the boundaries will be caught.

Original answer

You've coupled the class to SocketAsyncEventArgs where in fact all it needs is a function to assign the buffer, change SetBuffer to:-

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

Now you can call from consuming code something like this:-

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

I'm not sure that type inference is clever enough to resolve the types of buf, offset, size in this case. If not you will have to place the types in the argument list:-

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

However now your class can be used to allocate a buffer for all manner of requirements that also use the byte[], int, int pattern which is very common.

Of course you need to decouple the free operation to but thats:-

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

This requires you to call SetBuffer on the EventArgs in consuming code in the case for SocketAsyncEventArgs. If you are concerned that this approach reduces the atomicity of freeing the buffer and removing it from the sockets use, then sub-class this adjusted buffer manager and include SocketAsyncEventArgs specific code in the sub-class.

孤者何惧 2024-08-11 12:42:26

我用完全不同的方法创建了一个新类。

我有一个接收字节数组的服务器类。然后,它将调用不同的委托,将缓冲区对象交给它们,以便其他类可以处理它们。当这些类完成后,他们需要一种方法将缓冲区推回堆栈。

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

I've created a new class with a completely different approach.

I have a server class that receives byte arrays. It will then invoke different delegates handing them the buffer objects so that other classes can process them. When those classes are done they need a way to push the buffers back to the 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;
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文