【问题标题】:Array or ArraySegment as bufferArray 或 ArraySegment 作为缓冲区
【发布时间】:2013-03-12 08:24:46
【问题描述】:

在深入探讨问题本身之前,请理解,我无法控制这里的整个食物链 - “你做错了”类型的答案或 cmet 无济于事,因为我需要针对给定的 API。

传递原始数据块的组件通常通过byte[]ArraySegment<byte> 类型的缓冲区进行。后者的一大优势是能够一次性分配一个大缓冲区,然后通过 ArraySegment 机制使用部分缓冲区来减少内存分配、碎片和 GC 问题。这里没有什么新鲜事。

这是有代价的:使用ArraySegment<byte> 的(可能是外来的、封闭源代码的)组件获得对完整数组的引用(并因此获得访问权),这意味着,如果它行为不端,它就有能力破坏完全不相关的缓冲区。

现在:

  • 广泛的搜索显示没有任何机制可以将现有数组的一部分公开为完整数组,this SO question 是我得到的最接近的数组,但仍然无法使用,因为它“不是数组” .
  • 不支持从System.Array 继承。
  • 也不支持从System.ArraySegment<T> 继承。

我的问题链是(回答一个过时的问题):

  • 是否有一种“棘手”的方式将自行开发的包装器以byte[] 的形式呈现给(外国)消费者
  • 或者是否有一种“棘手”的方式将自行开发的包装器作为ArraySegment<byte> 呈现给(外国)消费者,这不会暴露完整的底层数组
  • 或者,有没有办法从System.Array 继承,我错过了
  • 再次交替:有没有办法从我错过的已分配(和固定)内存区域创建byte[],如果它被 GCed 不会搞砸

编辑:

从我的评论来看,我没有表达足够的表达。来回复制数据不是解决方案。不过是我现在用的拐杖。

【问题讨论】:

  • 我认为,复制任何数据都不是解决方案?
  • @DanielHilgarth 这就是我现在所做的,不,不是。性能影响很大(如果不是瘫痪的话)
  • @DanielHilgarth 我非常不愿意离开托管世界(即真正本地化),但不安全的代码是可以的。
  • 我同时删除了那条评论,因为我不确定这会有什么帮助。
  • 第三方代码实际期望的类型是什么? byte[]ArraySegment<byte>?

标签: .net buffer bytearray


【解决方案1】:

编辑:澄清一下,您的选择是

  • 重写外部库以不硬编码T[]/ArraySegment<T>
  • 选择不同的方式对缓冲区进行分段,以便对外部库的调用自然与缓冲区大小保持一致
  • 接受过大的缓冲区会暴露额外的数据,这些数据容易被外部代码破坏
  • 复制数据

编辑 2:毫无疑问

是否有一种“棘手”的方式将自行开发的包装器作为 byte[] 呈现给(外国)消费者

不,你不能这样做。

或者是否有一种“棘手”的方式将自行开发的包装器作为 ArraySegment 呈现给(外国)消费者,不会暴露完整的底层数组

不,你不能这样做。

另外,有没有办法从 System.Array 继承,我错过了

不,你不能这样做。

再一次:有没有办法从一个已经分配(和固定)的内存区域创建一个字节[],我错过了,如果它是 GCed 就不会搞砸

不,你不能这样做。


在不改变外部 API 的情况下,这变成了原来的答案:

如果您在使用数组时遇到问题并希望阻止其他代码访问超出允许范围的数组部分,则必须执行以下操作:

T[] originalData = ...;
int viewStart = ...;
int viewLength = ...;

// use a separate array for the viewable region
T[] view = new T[viewLength];
Array.Copy(originalData, viewStart, view, 0, viewLength);
ArraySegment<T> segment = new ArraySegment<T>(view).

// call other code
...

// apply changes
Array.Copy(view, 0, originalData, viewStart, viewLength);

由于 API 设计者选择使用 T[]/ArraySegment&lt;T&gt; 而不是 IList&lt;T&gt;,因此您受到了这些限制。

【讨论】:

  • 正如我在我的问题中提到的:“来回复制数据不是解决方案。不过这是我现在使用的拐杖。”
  • 除此之外,我目前为每个线程使用一个(足够大的)临时数组,以避免为每个调用分配和 GC。以给定的方式进行操作将是又一步。
  • @EugenRieck 我没有看错你的问题。您提出的问题没有解决方案,因此我发布了解释(另请注意编辑)。
  • 抱歉这么说:#1、3、4 明确排除在问题中,#2 是问题。请详细说明如何实现这一点!
  • @EugenRieck 你没有问#2。 #2 意味着一开始就不要分配这么大的缓冲区。不要分配过大的缓冲区,而是根据调用外部 API 所需的确切大小分配缓冲区。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-13
  • 2020-09-05
  • 1970-01-01
  • 2011-04-16
  • 2013-06-14
  • 2021-09-01
相关资源
最近更新 更多