【问题标题】:How to detect when a PipeReader has reached the end of the data source (end of pipe)?如何检测 PipeReader 何时到达数据源的末端(管道末端)?
【发布时间】:2020-01-25 09:03:02
【问题描述】:

我们可以调用 ReadAsync() 并检查缓冲区中的读取字节...

PipeReader reader = ...;
ReadResult readResult = await reader.ReadAsync();
ReadOnlySequence<byte> buffer = readResult.Buffer;
long availableBytes = buffer.Length

如果在调用 ReadAsync 之前长度没有增加,这是否表明管道结束(没有更多字节要读取)?如果不是,那么检测“管道末端”的正确方法是什么?

我们可以像这样表示缓冲区中字节的消耗:

reader.AdvanceTo(count);

然后检查是否有任何未消耗的字节,或者未来提供字节的可能性(即生产者尚未发出信号,它已停止向管道添加新字节)

readResult.IsCompleted

但是,如果我在缓冲区中寻找一个(或多个)序列,并在使用它之前等待一个完整的序列,那么即使缓冲区包含所有可用字节并且生产者已发出信号,IsComplete 似乎仍为假完成。

谢谢。

【问题讨论】:

  • "没有更多字节要读取" != "管道结束";只有当管道知道底层输入是关闭时,它们才是相同的,即不再有数据永远到达

标签: .net system.io.pipelines


【解决方案1】:

.IsCompleted 确实表示管道的结束,在某种意义上,套接字等被关闭(而不是打开但现在没有更多数据);我预计这里发生的事情是你:

  • 获取读取缓冲区
  • 读取整个缓冲区以查找序列,但未找到
  • 因此处理零字节
  • 因此说.AdvanceTo(zero)

AdvanceTo 还有一个重要的第二个重载——你不应该只告诉它你消费了什么;你应该告诉它你检查了什么,在这种情况下可能是:everything;这样做可以避免你陷入一个热循环,一遍又一遍地解析同一个不完整的帧。例如,我的一个读取循环看起来像(简化):

while (true)
{
    var readResult = await input.ReadAsync();
    var buffer = readResult.Buffer;
    int handled = TryConsume(ref buffer); // note: this **changes** buffer, slicing (Slice)
    // data from the start; when TryConsume exits, it will contain everything
    // that is *left*, but we will have effectively examined all of it; we will
    // have consumed any complete frames that we can from it

    // advance the pipe
    input.AdvanceTo(buffer.Start, buffer.End);

    // exit if we aren't making progress, and nothing else will be forthcoming
    if (handled == 0 && readResult.IsCompleted)
    {
        break; // no more data, or trailing incomplete messages
    }
}

【讨论】:

  • 谢谢马克。是的,我没有注意到带有“已检查”位置参数的 AdvanceTo() 重载。好的,所以如果我的序列检测最终未能检测到任何东西,那么我很想“倒带”检查的位置,否则如果/当相同的 PipeReader 传递给后续例程以供进一步阅读时,该位置是潜在状态。我试过了,它似乎工作正常 - 你认为这是正确/好的(预期/正常模式)?
  • @redcalx 你有可能最终陷入热循环;首选的方法是声明你没有取得进展,告诉它你检查了什么——即:一切;我不知道它是否曾经实现过,但是:在有关此 API 的早期讨论中,建议如果您未能取得相同的进展两次(即如果您看到相同的缓冲区并报告 0/0),则该实现假定您只是使用错误,并退出直到有更多数据;也许这能救你?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-11-24
  • 1970-01-01
  • 1970-01-01
  • 2018-09-17
  • 1970-01-01
  • 2015-07-12
  • 1970-01-01
相关资源
最近更新 更多