我正在学习C#异步套接字编程,并且我已经了解到在某种池中重用字节缓冲区是个好主意,然后在从套接字接收数据时根据需要检查一个。
但是,我已经看到了两种不同的字节数组池方法:一种是使用简单的队列系统,只是根据需要在队列中添加/删除它们。如果请求了一个并且队列中没有剩余,则会创建一个新的字节数组。
我见过的另一种方法是为整个程序使用一个大字节数组。队列的概念仍然适用,但它是一个整数队列,它确定要使用的字节数组的片(偏移量)。如果请求了一个并且队列中没有剩余,则必须调整数组的大小。
对于高度可扩展的服务器,哪一个是更好的解决方案?我的直觉是,使用多个字节数组会更便宜,因为我想要根据需要调整数组大小(即使我们在大块中分配它)也会非常昂贵,特别是当它变大时。使用多个数组似乎也更直观 - 使用一个我没想过的大型数组有什么优势吗?
答案 0 :(得分:5)
你的直觉是正确的。每次需要使数组变大时,您将重新创建数组并复制现有的字节。由于我们在这里讨论字节,因此数组的大小可能会很快变大。因此,每次都会要求连续的内存,这取决于程序使用内存的方式,可能或不可行。这也将成为一个虚拟池,可以这么说。根据定义,池具有一组由多个客户端管理和共享的多个项目。
一种阵列解决方案实施起来也更复杂。好处是,一个阵列解决方案允许你提供可变大小的块,但这是以实际上重新实现malloc为代价的:处理碎片等等,你不应该进入。
多阵列解决方案允许您使用N个缓冲区初始化池,并以简单的方式轻松管理它们。绝对是我推荐的方法。
答案 1 :(得分:2)
我不建议调整大小选项。从简单开始,一路向上。一个字节缓冲区队列,当它耗尽时会得到一个新的字节缓冲区,这将是一个好的开始。您可能需要注意线程问题,因此我的建议是使用其他人的线程安全队列实现。
接下来你可以看看更复杂的“指针”到一个大字节数组块,除了我的建议是有一个4k / 16k的队列(一些功能为页面大小的两倍)块阻止你index into,当它已满时,你将另一个大块添加到队列中。实际上,由于性能的复杂性和不确定性,我完全不建议这样做。
从简单开始,按照自己的方式行事。缓冲池,使其线程安全,看看你是否还需要更多。
答案 2 :(得分:2)
多个缓冲区再投票,但由于您正在异步执行操作,因此需要确保您的队列是线程安全的。默认的Queue<T>
集合肯定是不线程安全。
SO用户和MS员工JaredPar在这里有一个很好的线程安全队列实现:
http://blogs.msdn.com/jaredpar/archive/2009/02/16/a-more-usable-thread-safe-collection.aspx
答案 3 :(得分:1)
如果您使用单个缓冲区,则需要一个策略,以便在需要时它的增长速度。如果以较小的增量增长,则可能需要经常进行并经常复制所有数据。如果你以较大的增量增长它(比如下一个大小是前一个大小的1.5倍),当你得到“内存不足”而只是试图增加缓冲区时,你就有可能遇到这种情况。对于可扩展的系统来说,这是一个双输的选择。这就是重复使用小缓冲区的原因。
答案 4 :(得分:1)
使用垃圾收集堆时,您应该始终使用寿命较短的小型,正确大小的缓冲区。 .NET堆分配器非常快,第0代集合非常便宜。
当您保留静态缓冲区时,您将在程序的生命周期中耗尽系统资源。最糟糕的情况是当它变得足够大以便在大物体堆中移动时,它将成为永久性障碍,无法移动。