【问题标题】:C++ stdin occasionally garbledC++ 标准输入偶尔会出现乱码
【发布时间】:2015-08-18 12:55:36
【问题描述】:

在过去的几天里,我遇到了一个奇怪的偶尔发生的错误。

我有一个控制台应用程序,它还显示一个使用 SDL 打开的窗口,用于连续运行三个线程的图形输出。主线程运行事件循环,并处理控制台输入。第二个线程使用std::cin.getline 获取控制台输入。然而,第二个线程也负责输出日志信息,这些信息可以在用户单击 SDL 窗口上的某个位置时生成。

这些日志消息被发送到受互斥体保护的stringstream,线程 2 定期检查。如果有日志消息,它会删除提示,输出它们,然后打印一个新提示。由于这个原因,它不能在getline 上阻塞,所以这个线程产生第三个线程peeks cin 并在有数据要从输入流中获取时通过atomic 发出信号,其中点getline被调用并将输入传递给主线程上的逻辑。

这是我还没有完全解决的问题,其中大约 30 个失败,因为程序没有收到与终端输入完全相同的输入。你可以在图片here 中看到我的意思,第一行是类型,第二行是 Lua 堆栈跟踪,因为接收到不同(不正确)的输入。

无论我是否使用rlwrap,都会发生这种情况。这是因为peekgetline 同时命中输入流吗? (这是可能的,因为 peek 循环看起来像:

while(!exitRequested_)
{
  if (std::cin.peek())
    inputAvailable_ = true; // this is atomic

  std::this_thread::sleep_for(std::chrono::milliseconds(10));
}

有什么想法吗?我快速查看了curses,但看起来使用起来相当费劲。我以前从未听说过获取线路乱码的东西。但我也打印了一段时间内收到的每个字符串,它们与 Lua 报告的内容相匹配。

【问题讨论】:

  • peek 似乎会干扰getline。我不确定 C++ 标准对 IO 流的线程安全性有何规定,但即使它应该是线程安全的,库中也可能存在错误。是否可以在 peeking 线程和实际输入的线程之间使用双向通信? IE。窥视线程设置inputAvailable,并且只有在被另一个线程再次清除后(在它调用getline之后)才恢复窥视。无论如何,这可能值得一试。
  • @davmac 这似乎已经修复了它,我还不能确定,因为该错误通常只是很久没有出现(我想是线程错误的性质)。但是我尝试了很多行(100+),没有错误,看起来很有希望。我真的不知道为什么我以前没有想到这一点。我最初有一对布尔做这项工作(一个需要是真的,一个是假的),一个糟糕的想法 - 他们一直在没有输入的情况下离开程序,所以我把那个锁拿走了,我认为这就是这个问题出现的时候。所以也许只有一个人可以解决问题。
  • 明确禁止同时访问同一个流对象。
  • @DavidSchwartz 谢谢,很高兴知道这一点,我会将其添加到答案中。
  • 您是否考虑过将显示到控制台和从标准输入读取的职责拆分到单独的线程中?您甚至可以通过让新线程简单地将行复制到缓冲区中来保持大致相同的设计,例如deque<string>,因此对现有线程的唯一更改是用查询缓冲区替换来自cin 的读取以查看是否存在内容。 (你甚至可以在条件变量上使用定时等待而不是休眠和轮询,以便获得更好的响应时间)

标签: c++ multithreading stdin


【解决方案1】:

正如@davmac 所建议的,peek 似乎一直在干扰getline。我的假设是这与peek 获取一个字符,然后在getline 获取缓冲区的同时将其放回。

无论问题的根本原因是什么,我都 >98% 确定该问题已通过实施 davmac 建议的修复程序得到解决。 在几个小时的使用中,我没有遇到任何问题。

道德,不要同时访问cin,即使其中一个函数没有修改流。

(请注意,上述情况同时发生在 g++ 和 clang++ 上,所以我假设它只是与经常实现 std 库的方式相关联)。

正如@DavidSchwartz 指出的那样,明确禁止并发访问流,因此清楚地解释了修复工作的原因。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-08
    相关资源
    最近更新 更多