2

我正在学习 C# 异步套接字编程,并且我了解到在某种池中重用字节缓冲区是一个好主意,然后在从套接字接收数据时根据需要检查一个。

但是,我看到了两种不同的字节数组池方法:一种使用简单的队列系统,然后根据需要从队列中添加/删除它们。如果请求了一个并且队列中没有剩余,则创建一个新的字节数组。

我见过的另一种方法对整个程序使用一个大字节数组。队列的想法仍然适用,但它是一个整数队列,它确定要使用的字节数组的切片(偏移量)。如果请求了一个并且队列中没有剩余,则必须调整数组的大小。

对于高度可扩展的服务器,哪一个是更好的解决方案?我的直觉是只使用许多字节数组会更便宜,因为我想根据需要调整数组的大小(即使我们以大块的形式分配它)会非常昂贵,尤其是当它变大时。使用多个数组似乎也更直观——使用一个我没有想到的大型数组有什么好处吗?

4

5 回答 5

5

你的直觉是正确的。每次需要使数组更大时,您都将重新创建数组并复制现有字节。由于我们在这里讨论字节,因此数组的大小可能会很快变大。因此,您每次都会要求一块连续的内存,这取决于您的程序如何使用内存,可能可行也可能不可行。这实际上也将成为一个虚拟池,可以这么说。根据定义,池具有一组由各种客户端管理和共享的多个项目。

单一阵列解决方案实施起来也更加复杂。好消息是一个数组解决方案允许您提供可变大小的块,但这是以重新实现 malloc 为代价的:处理您不应该进入的碎片等。

多阵列解决方案允许您使用 N 数量的缓冲区初始化池,并以简单的方式轻松管理它们。绝对是我推荐的方法。

于 2009-02-24T14:43:05.377 回答
2

我不建议调整大小选项。从简单开始,一路向上。一个字节缓冲区队列在耗尽时将一个新的缓冲区添加到末尾将是一个好的开始。您可能必须注意线程问题,所以我的建议是使用其他人的线程安全队列实现。

接下来,您可以查看指向大字节数组块的更复杂的“指针”,除了我的建议是有一个 4k/16k(页面大小的两倍的幂)块的队列,您可以索引到,当它已满时,您将另一个大块添加到队列中。实际上,由于复杂性和性能上的可疑增益,我根本不推荐这样做。

从简单开始,一路向上。缓冲区池,使其线程安全,看看你是否需要更多。

于 2009-02-24T14:44:11.163 回答
2

对多个缓冲区再投一票,但另外,由于您正在异步执行操作,因此您需要确保您的队列是线程安全的。默认Queue<T>集合绝对不是线程安全的。

SO用户和MS员工JaredPar在这里有一个很好的线程安全队列实现:http:
//blogs.msdn.com/jaredpar/archive/2009/02/16/a-more-usable-thread-safe-collection.aspx

于 2009-02-24T14:50:01.430 回答
1

如果您使用单个缓冲区,您需要一个策略来确定它在需要时应该以多快的速度增长。如果您以较小的增量增长它,您可能必须经常这样做并经常复制所有数据。如果您以较大的增量增长它(例如下一个大小是前一个大小的 1.5 倍),您可能会面临“内存不足”的情况,而只是试图增加缓冲区。对于可扩展系统来说,这是一个双输的选择。这就是为什么重用小缓冲区更可取的原因。

于 2009-02-24T14:58:06.730 回答
1

使用垃圾收集堆,您应该始终偏爱具有较短生命周期的小型、大小合适的缓冲区。.NET 堆分配器非常快,第 0 代集合非常便宜。

当您保留一个静态缓冲区时,您将在程序的生命周期内耗尽系统资源。最坏的情况是当它大到可以在大对象堆中移动时,它将成为无法移动的永久障碍。

于 2009-02-24T16:01:22.023 回答