C ++程序在输入到控制台时停止生成控制台输出

时间:2018-02-22 12:20:25

标签: c++ console output interaction

我有一个C ++程序(MSVC 2017),它通过std :: cout不断输出调试信息。但是,有时当我与控制台进行物理交互时(例如,意外点击它),它会停止产生输出。这意味着没有任何东西被打印出来,虽然程序继续运行并完成正常运行的任何事情。

任何想法如何解决这个问题?使用" std :: cout.setf(std :: ios :: unitbuf)删除std :: cout缓冲区;"没有效果。

样品:

#include <iostream>

int main()
{
  int i = 0;
  while (true) {
    i++;
    if (i%100000000 == 0) std::cout << i++ << "\n";
  }
  return 0;
}

1 个答案:

答案 0 :(得分:3)

这就是我为重现测试而做的 - 编写mcve.cc

#include <iostream>
int main()
{
  for (char i = 0;; ++i) std::cout << (int)i << std::flush;
  return 0;
}

我在VS2013(调试模式)中编译并启动。它开始“吹嘘”数字。

我点击进入控制台窗口,输出停止(如OP所述)。按 ESC 之后,我预计会有更多数字,但没有任何反应。

我暂停了调试并查看了调用堆栈,但没有什么特别的。踩了一下,看起来代码仍然执行。 (正如我在调试器的自动显示中看到的那样,i的计数仍然发生。)

所以,我开始应用另一个Q / A SO: How to disable user selection in Windows console的解决方案。虽然,它似乎是值得的,但不是MCVE。所以,我必须使用谷歌和MSDN完成它:

#include <iostream>
#include <Windows.h>
int main()
{
  // disable QuickEdit mode in Console
  HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE);
  DWORD prev_mode;
  GetConsoleMode(hInput, &prev_mode); 
  SetConsoleMode(hInput, prev_mode & ~ENABLE_QUICK_EDIT_MODE);
  // start test
  for (char i = 0;; ++i) std::cout << (int)i << std::flush;
  // done (never reached)
  return 0;
}

这有效 - 禁用了QuickEdit。 (单击控制台窗口不再停止输出。)

然而,如果没有这个技巧,它应该也可以。 (这让我感到困扰,我不理解这一点。)经过一段时间的思考,我找到了令人愉快的想法。可能是QuickEdit之后std::coutbad()吗?

所以,我做了第三个版本。因为我无法使用我在调试器中可以看到的cout把我修改过的i。 (实际上,std::cout::good()的返回也被显示,但是对i的分配更具说明性。)

#include <iostream>
#include <Windows.h>
int main()
{
  for (char i = 0;; ++i) {
    if (!std::cout.good()) i = 0;
    std::cout << (int)i << std::flush;
  }
  return 0;
}

在QuickEdit选择和 ESC 之后,i始终为0。因此,另一个修复是显而易见的:std::cout应定期clear()

#include <iostream>
#include <Windows.h>
int main()
{
  for (char i = 0;; ++i) {
    if (!std::cout.good()) std::cout.clear();
    std::cout << (int)i << std::flush;
  }
  return 0;
}

我不确定我更喜欢哪两种解决方案:

  • 前者侵入性最小(只是main()开头的一个补充。)
  • 后者是纯C ++(没有特定于平台的代码),我更喜欢这一点。

有关非Windows平台的评论会很有趣......

我不记得我曾经在Linux上看到过这样的QuickEdit问题(也不是Irix或Solaris - 我过去曾经使用过的操作系统)。在那个系统上,Xterm / X11处理了选择(在我的例子中) - 超出了流I / O的范围。

那么,std::cout甚至可能在那些系统上变坏(假设输出中没有编码错误)?

最后,我发现了一种便携式非侵入式方法(以多线程为代价):

#include <atomic>
#include <iostream>
#include <thread>

int main()
{
  // spawn extra thread to clean cout periodically
  std::atomic<bool> exitThreadClearCOut = false;
  std::thread threadClearCOut([&]() {
    while (!exitThreadClearCOut) {
      if (!std::cout.good()) std::cout.clear();
      std::this_thread::sleep_for(std::chrono::milliseconds(100));
      // 100 ms - nearly non-perceptable for humans but an "eternity" for modern CPUs
    }
  });
  // start main work
  for (char i = 0;; ++i) {
    std::cout << (int)i << std::flush;
  }
  // finish/join thread to clean cout periodically
  exitThreadClearCOut = true;
  threadClearCOut.join();
  // done
  return 0;
}

它开始一个额外的线程来定期检查/清除std::cout。这是必须仅添加到main()(我认为是“非侵入性修复”)的其他内容 - 实际的代码库不需要更改。

注意:我有点怀疑对std::cout的并发访问是否安全(尽管我相信会记得它)。对此,我发现了另一个Q / A SO: Is cout synchronized/thread-safe?。根据此链接中接受的答案,从C ++ 11开始保证(或至少需要)。