我正在研究使用管道处理来自网络的二进制消息的可能性。
我将处理的二进制消息带有有效负载,并且希望将有效负载保持为二进制形式。
这个想法是读出整个消息并创建一个消息片及其有效负载,一旦消息被完全读取,它将被传递到通道链进行处理,处理不会是即时的,可能需要一些时间或被执行稍后目标是不要让管道读取器等待直到处理完成,然后一旦消息处理完成,我需要将处理后的缓冲区释放给管道编写器。
当然,现在我可以创建一个新的字节数组并复制来自管道编写器的数据,但这会超出无复制的目的吗?据我了解,我需要管道和通道之间的一些缓冲区同步?
我观察了可用的 api (前进到)管道读取器,可以告诉管道读取器消耗了什么以及检查了什么,但无法绕过如何在管道读取方法之外同步。
所以问题是是否有一些技术或例子来说明如何实现这一点。
缓冲区获得自TryRead
/ReadAsync
仅在您致电之前有效AdvanceTo
,与期待一旦您完成此操作:您报告为消耗的任何内容都可以回收以供其他地方使用(可能是并行/并发读取器)。严格来说:即使是你的部分haven't报告为已消耗:一旦您致电,您仍然不应将其视为有效AdvanceTo
(尽管实际上,它们很可能仍然是相同的段 - 只是:这不是调用者关心的问题;对于调用者来说,它仅在读取和前进之间有效)。
这意味着您明确不能执行以下操作:
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
}
}
因为“在后台”位,当它触发时,现在可以读取其他人的数据(因为它已经被重用)。
So: either您需要将帧内容作为读取循环的一部分进行处理,or您将必须复制数据,很可能使用:
c#
var len = checked ((int)buffer.Length);
var oversized = ArrayPool<byte>.Shared.Rent(len);
buffer.CopyTo(oversized);
并通过oversized
到你的后台处理,记住只看第一个len
它的字节。你could将其传递为ReadOnlyMemory<byte>
,但是您需要考虑到之后您还想将其返回到数组池(可能在finally
块),并将其作为内存传递会使它变得有点尴尬(但并非不可能,这要归功于MemoryMarshal.TryGetArray
).
注意:在管道 API 的早期版本中,有一个引用计数的元素,它did允许您保留缓冲区,但它有一些问题:
- 它使 API 变得非常复杂
- 它导致缓冲区泄漏
- “保留”的含义含糊不清且令人困惑;是直到得到为止的计数reused? or 完全释放?
所以这个功能被删除了。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)