【问题标题】:Accumulating backlog on std::cin causes it to lose data在 std::cin 上累积积压会导致它丢失数据
【发布时间】:2025-12-31 17:05:02
【问题描述】:

我有一个外部二进制文件,它向标准输出产生无限的二进制数据流 (~10 MiB/s),我正在尝试编写一个 C++ 程序来使用它。我的消费者程序有一个不寻常的模式:它首先从流中读取少量数据(~32KiB),处理大约一分钟,然后以更快的速度(> 50 MiB/s)再次开始消费数据.

我正在调用程序如下:$ my_producer | my_consumer

因为消费者最初比消费者慢得多,我预计管道将建立大约60s * 10 MiB/s = 600 MiB 的积压。然而,在一分钟的初始延迟之后,我的消费者开始以大约 10MiB/s 的速度消费数据,这意味着在一分钟间隔内产生的数据丢失了;为什么?

消费者的相关代码是这样的:

std::vector<char> StreamSource::Read(std::size_t size) {
  auto data = std::vector<char>(size);
  stream_.read(data.data(), size);
  data.resize(stream_.gcount());
  assert(stream_.good() || stream_.eof());
  return data;
}

std::istream& stream_;  // Initialized to std::cin

有趣的是,写入文件然后将该文件传递给消费者可以按预期工作!

$ my_producer > ~/Desktop/data
$ cat ~/Desktop/data | my_consumer

我进行了一系列测试以确保我的制作人不会受到指责;以下失败,因为生产者检测到“短写”:

$ my_producer | throttle -M1 > ~/Desktop/data

我正在寻求有关如何解释缺失数据的建议。如果相关,我在 MacOS 上运行。

谢谢!

【问题讨论】:

    标签: c++ linux unix pipe


    【解决方案1】:

    我很确定没有操作系统允许管道建立高达 600mb,当管道已满时写入端将阻塞。如果您想允许建立如此多的数据,您需要在您的应用程序中有一个线程连续从cin 读取并缓冲数据,然后您的应用程序可以从该缓冲区而不是cin 读取。

    根据https://unix.stackexchange.com/questions/11946/how-big-is-the-pipe-buffer,管道一般最大为64K。

    【讨论】:

    • 感谢您的快速回复,艾伦!我同意 - 一个大小有问题的缓冲区,将数据读入我的消费者可能会解决它。我仍然想了解正在发生的事情,以及如何检测到这一点。写入似乎不太可能被阻塞,因为生产者没有失败(参见我的throttle 示例)。谁在丢弃这些字节?
    • 没有minimal reproducible example 很难分辨。生产者完全有可能有一个单独的线程来生成数据来写入数据,如果数据写入速度不够快,它就会丢弃它