为什么从管道读取时 libc++ getline 会阻塞,而 libstdc++ getline 不会?

2024-03-01

TL;DR

一个程序使用libc++的版本getline当函数从管道读取输入时,它将阻塞,直到管道的缓冲区已满。

相同的是NOT为真libstdc++的版本getline函数:这里,一旦输入行可用,该函数就会立即读取并返回一行输入。

我是否应该预料到两者之间的行为差​​异libstdc++ and libc++? [EDIT:我不是在这里征求意见,我只是对管道了解不够,也不了解实现 C++ 标准库的困难。对我来说,这种行为上的差异肯定是was令人惊讶,但也许有人更了解并且可以向我保证这种差异是可以预料的,也许这只是一个实现细节?]

更重要的是,我能做些什么来让libc++表现得像libstdc++做?也就是说,getline函数不应等到管道缓冲区已满,它应在可用时立即返回一行输入。

请参阅下面的代码示例,该示例显示了我如何读取和写入管道。

环境

  • macOS 10.13.1(高塞拉利昂)
  • Xcode 9.1
  • Apple LLVM 版本 9.0.0 (clang-900.0.38)

我怀疑这个问题不仅限于 macOS,但我还没有 Linux 开发系统clang我可以用它来测试。

测试准备

打开三个外壳,我们称它们为 A、B 和 C。

在 shell A 中:创建一个新文件pipe-test.cpp并添加下面的源代码。编译源代码一次libstdc++和一次与libc++:

g++ -stdlib=libstdc++ -o pipe-test-libstdc++ pipe-test.cpp
g++ -stdlib=libc++ -o pipe-test-libc++ pipe-test.cpp

在 shell A 中:创建两个管道:

mkfifo input-pipe output-pipe

测试1,使用libstdc++程序版本

  • 在 shell A 中,运行以下命令:pipe-test-libstdc++ input-pipe output-pipe
  • 在 shell B 中,运行以下命令:cat output-pipe
  • 在 shell C 中,运行以下命令:cat >input-pipe
  • 在 shell C 中,键入行“foo”并按 ENTER
  • 切换到 shell B:您将看到字符串“foo”。
  • 发生了什么?在 shell A 中运行的程序已使用以下命令从输入管道读取了行“foo”getline函数并立即将该行打印到输出管道。
  • 在 shell C 中,键入 CTRL+D。测试结束,所有 shell 现在都应该返回命令行。

测试2,使用libc++程序版本

  • 在 shell A 中,运行以下命令:pipe-test-libc++ input-pipe output-pipe
  • 在 shell B 中,运行以下命令:cat output-pipe
  • 在 shell C 中,运行以下命令:cat >input-pipe
  • 在 shell C 中,键入行“foo”并按 ENTER
  • 切换到 shell B:你会NOT看到字符串“foo”。
  • 发生了什么?在 shell A 中运行的程序仍然处于阻塞状态,getline函数尚未从输入管道接收到行“foo”,因为该管道的缓冲区尚未满。
  • 在 shell C 中,键入 CTRL+D。
  • 切换到外壳B:NOW你会看到字符串“foo”。
  • 请注意,您也可以将大量文本粘贴到 shell C 中,而不是键入 CTRL+D。一旦管道的缓冲区已满,getline函数将开始逐行读取输入,直到清空管道的缓冲区。

源代码

#include <string>
#include <iostream>
#include <fstream>


int main(int argc, char** argv)
{
  if (argc != 3)
  {
    std::cout
      << "Usage: pipe-test /path/to/input-pipe /path/to/output-pipe"
      << std::endl;
    return 1;
  }

  std::string pathToInputPipe = argv[1];
  std::string pathToOutputPipe = argv[2];

  std::cout
    << "Input pipe = " << pathToInputPipe << std::endl
    << "Output pipe = " << pathToOutputPipe << std::endl;

  std::ifstream inputPipeStream;
  inputPipeStream.open(pathToInputPipe.c_str());
  if (! inputPipeStream)
  {
    std::cout
      << "Failed to open input pipe " << pathToInputPipe
      << std::endl;
    return 1;
  }
  else
  {
    std::cout
      << "Input pipe: result of eof() = " << inputPipeStream.eof() << std::endl
      << "Input pipe: result of bad() = " << inputPipeStream.bad() << std::endl
      << "Input pipe: result of good() = " << inputPipeStream.good() << std::endl
      << "Input pipe: result of fail() = " << inputPipeStream.fail() << std::endl;
  }

  std::ofstream outputPipeStream;
  outputPipeStream.open(pathToOutputPipe.c_str());
  if (! outputPipeStream)
  {
    std::cout
      << "Failed to open output pipe " << pathToOutputPipe
      << std::endl;
    return 1;
  }
  else
  {
    std::cout
      << "Output pipe: result of eof() = " << outputPipeStream.eof() << std::endl
      << "Output pipe: result of bad() = " << outputPipeStream.bad() << std::endl
      << "Output pipe: result of good() = " << outputPipeStream.good() << std::endl
      << "Output pipe: result of fail() = " << outputPipeStream.fail() << std::endl;
  }

  int lineNumber = 0;
  while (true)
  {
    lineNumber++;
    std::string line;
    bool getlineFailResult = getline(inputPipeStream, line).fail();
    if (getlineFailResult)
    {
      std::cout << "Failed to read stream, stopping" << std::endl;
      break;
    }
    else
    {
      std::cout << "Received line " << lineNumber << ": " << line << std::endl;
      outputPipeStream << line << std::endl;
      outputPipeStream.flush();
    }
  }

  return 0;
}

您可能知道,libc++ 是标准库的非 GPL (MIT) 版本。 libstdc++ 是 GPL 版本。 libc++ 是 LLVM 的一部分。 libstdc++ 是一个 GPL 库,通常随 GCC 一起提供。

它们是两种完全不同的实现。哪个更好,或者您应该使用哪个是一种意见。有很多关于此的线程,例如:我应该使用 libc++ 还是 libstdc++? https://stackoverflow.com/questions/14972425/should-i-use-libc-or-libstdc

我无法轻易找到有关 getline() 的规范来指定您所关心的行为。

这是 libc++ 版本中 getline 的源代码https://github.com/llvm-mirror/libcxx/blob/018a3d51a47f7275c59e802709104498b729522b/include/istream#L1037 https://github.com/llvm-mirror/libcxx/blob/018a3d51a47f7275c59e802709104498b729522b/include/istream#L1037

他们的行为不同这一事实并不令我感到惊讶。如果您需要 getline() 的特定实现/行为,我要么编写您自己的,因为它不是一个复杂的操作,并使用标准 POSIX 系统调用来执行它,这些调用是规范的,并且应该是相同的 - 无论操作系统或编译器。

另外(这里的意见),我的建议是避免使用标准 C++ 库,尤其是 iostream,除了实现之外,除非它们已经在大型代码库中并且您必须使用它们。它们充满了错误,有不同的行为,而且通常速度不快。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么从管道读取时 libc++ getline 会阻塞,而 libstdc++ getline 不会? 的相关文章

  • Qt - 无法让 lambda 工作[重复]

    这个问题在这里已经有答案了 我有以下功能 我想在其中修剪我的std set
  • try-catch 中未处理的异常

    try list from XElement e in d Descendants wix File where e Attribute Name Value Contains temp Name e Parent Parent Attri
  • TextBox 焦点的 WinForms 事件?

    我想添加一个偶数TextBox当它有焦点时 我知道我可以用一个简单的方法来做到这一点textbox1 Focus并检查布尔值 但我不想那样做 我想这样做 this tGID Focus new System EventHandler thi
  • ZLIB 解压缩

    我编写了一个小型应用程序 该应用程序应该解压缩以 gzip deflate 格式编码的数据 为了实现这一点 我使用 ZLIB 库 使用解压缩功能 问题是这个功能不起作用 换句话说 数据不是未压缩的 我在这里发布代码 int decompre
  • 为什么密码错误会导致“填充无效且无法删除”?

    我需要一些简单的字符串加密 所以我编写了以下代码 有很多 灵感 来自here http www codeproject com KB security DotNetCrypto aspx create and initialize a cr
  • C++11 函数局部静态 const 对象的线程安全初始化

    这个问题已在 C 98 上下文中提出 并在该上下文中得到回答 但没有明确说明有关 C 11 的内容 const some type create const thingy lock my lock some mutex static con
  • 如何用 kevent() 替换 select() 以获得更高的性能?

    来自Kqueue 维基百科页面 http en wikipedia org wiki Kqueue Kqueue 在内核和用户空间之间提供高效的输入和输出事件管道 因此 可以修改事件过滤器以及接收待处理事件 同时每次主事件循环迭代仅使用对
  • 在 C# 中将位从 ulong 复制到 long

    所以看来 NET 性能计数器类型 http msdn microsoft com en us library system diagnostics performancecounter aspx有一个恼人的问题 它暴露了long对于计数器
  • 禁用 LINQ 上下文的所有延迟加载或强制预先加载

    我有一个文档生成器 目前包含约 200 个项目的查询 但完成后可能会超过 500 个 我最近注意到一些映射表示延迟加载 这给文档生成器带来了一个问题 因为它需要根据生成的文档来访问所有这些属性 虽然我知道DataLoadOptions可以指
  • 单元测试失败,异常代码为 c0000005

    我正在尝试使用本机单元测试项目在 Visual Studios 2012 中创建单元测试 这是我的测试 TEST METHOD CalculationsRoundTests int result Calculations Round 1 0
  • 范围和临时初始化列表

    我试图将我认为是纯右值的内容传递到范围适配器闭包对象中 除非我将名称绑定到初始值设定项列表并使其成为左值 否则它不会编译 这里发生了什么 include
  • 事件日志写入错误

    很简单 我想向事件日志写入一些内容 protected override void OnStop TODO Add code here to perform any tear down necessary to stop your serv
  • C# 创建数组的数组

    我正在尝试创建一个将使用重复数据的数组数组 如下所示 int list1 new int 4 1 2 3 4 int list2 new int 4 5 6 7 8 int list3 new int 4 1 3 2 1 int list4
  • C# using 语句、SQL 和 SqlConnection

    使用 using 语句 C SQL 可以吗 private static void CreateCommand string queryString string connectionString using SqlConnection c
  • 32位PPC rlwinm指令

    我在理解上有点困难rlwinmPPC 汇编指令 旋转左字立即然后与掩码 我正在尝试反转函数的这一部分 rlwinm r3 r3 0 28 28 我已经知道什么了r3 is r3在本例中是一个 4 字节整数 但我不确定这条指令到底是什么rlw
  • 为什么 Ajax.BeginForm 在 Chrome 中不起作用?

    我正在使用 c NET MVC2 并尝试创建一个 ajax 表单来调用删除数据库记录 RemoveRelation 的方法 删除记录的过程正在按预期进行 删除记录后 表单应调用一个 JavaScript 函数 从视觉效果中删除该记录 Rem
  • Azure函数版本2.0-应用程序blobTrigger不工作

    我有一个工作功能应用程序 它有一个 blob 输入和一个事件中心输出 在测试版中工作 随着最新的更改 我的功能不再起作用 我尝试根据发行说明更新 host json 文件 但它没有引用 blob 触发器 version 2 0 extens
  • 从类模板参数为 asm 生成唯一的字符串文字

    我有一个非常特殊的情况 我需要为类模板中声明的变量生成唯一的汇编程序名称 我需要该名称对于类模板的每个实例都是唯一的 并且我需要将其传递给asm关键字 see here https gcc gnu org onlinedocs gcc 12
  • WPF/数据集:如何通过 XAML 将相关表中的数据绑定到数据网格列中?

    我正在使用 WPF DataSet 连接到 SQL Server Express XAML 和 C Visual Studio 2013 Express 我从名为 BankNoteBook 的现有 SQL Server Express 数据
  • 如何创建向后兼容 Windows 7 的缩放和尺寸更改每显示器 DPI 感知应用程序?

    我是 WPF 和 DPI 感知 API 的新手 正在编写一个在 Windows 7 8 1 和 10 中运行的应用程序 我使用具有不同每个显示器 DPI 设置的多个显示器 并且有兴趣将我的应用程序制作为跨桌面配置尽可能兼容 我已经知道可以将

随机推荐