从TryRead
/获得的缓冲区ReadAsync
仅在您调用 之前有效AdvanceTo
,期望一旦您完成此操作:您报告为消耗的任何内容都可以回收以在其他地方使用(可能是并行/并发读取器)。严格来说:即使您没有报告为消耗的位:一旦您调用,您仍然不应该将其视为有效AdvanceTo
(尽管实际上,它们很可能仍然是相同的段 - 只是:那是' t 调用者的关注;对调用者来说,它只在读取和提前之间有效)。
这意味着您明确不能这样做:
while (...)
{
var result = await pipe.ReadAsync();
if (TryIdentifyFrameBoundary(out var frame)) {
BeginProcessingInBackground(frame); // <==== THIS IS A PROBLEM!
reader.AdvanceTo(frame.End, frame.End);
}
else if { // take nothing
reader.AdvanceTo(buffer.Start, buffer.End);
if (result.IsCompleted) break; // that's all folks
}
}
因为“在后台”位,当它触发时,现在可能正在读取其他人的数据(由于它已经被重用)。
所以:要么您需要将帧内容作为读取循环的一部分进行处理,要么您将不得不制作数据的副本,很可能通过使用:
c#
var len = checked ((int)buffer.Length);
var oversized = ArrayPool<byte>.Shared.Rent(len);
buffer.CopyTo(oversized);
并传递oversized
给您的后台处理,记住只查看len
它的第一个字节。您可以将其作为 a 传递ReadOnlyMemory<byte>
,但您需要考虑到之后您还希望将其返回到数组池(可能在一个finally
块中),并将其作为内存传递会使其更加尴尬(但是并非不可能,感谢MemoryMarshal.TryGetArray
)。
注意:在管道 API 的早期版本中,有一个引用计数元素,它确实允许您保留缓冲区,但它有一些问题:
- 它极大地复杂化了 API
- 它导致缓冲区泄漏
- “保留”的含义模棱两可且令人困惑;在它被重用之前是计数吗?还是完全释放?
所以这个功能被删除了。