C++ 程序在向控制台输入时停止生成控制台输出
C++ program stops producing console output upon input to the console
我有一个 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;
}
这就是我重现测试所做的——写作 mcve.cc
:
#include <iostream>
int main()
{
for (char i = 0;; ++i) std::cout << (int)i << std::flush;
return 0;
}
我是在VS2013(调试模式)下编译启动的。它开始 "blow" 出数字。
我点击了控制台 window 并且输出停止了(如 OP 所述)。按 ESC 后,我预计会有更多数字,但什么也没发生。
我暂停了调试并查看了调用堆栈,但什么也没有 extra-ordinary。往前走一步,甚至看起来代码还在执行。 (i
的计数仍然发生,正如我在调试器的自动显示中看到的那样。)
所以,我开始应用另一个Q/A. Though, it appeared to be of worth it was no MCVE的解决方案。所以,我不得不使用 google 和 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 已禁用。 (点击控制台 window 不再停止输出。)
但是,如果没有这个技巧,它应该也能正常工作。 (我不明白这一点很困扰我。) 想了一会儿,我得出了一个启发性的想法。难道 std::cout
在 QuickEdit 之后是 bad()
?
所以,我做了第三个版本。因为我不能使用 cout
put,所以我修改了 i
,我可以在调试器中看到它。 (实际上,std::cout::good()
的 return 也显示了,但分配给 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()
ed:
#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++(没有特定于平台的代码)。
关于 non-Windows 平台的评论会很有趣...
我不记得我曾经在 Linux 上见过这样的 QuickEdit 问题(Irix 或 Solaris 也没有——我过去曾经使用过的操作系统)。在那个系统上,选择由 Xterm/X11 处理(在我的例子中)——超出了流 I/O.
的范围
那么,std::cout
是否有可能在那个系统上变坏(假设输出中没有编码错误)?
终于找到了一个可移植的non-invasive方法(代价是multi-threading):
#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
的周期性 check/clean。这是必须添加到 main()
的其他东西(我认为是 "non-invasive fix")——实际代码库不需要更改。
注意:我有点怀疑并发访问 std::cout
是否安全(尽管我相信记得它是安全的)。关于这个,我又找了一个Q/ASO: Is cout synchronized/thread-safe?。根据此 link 中接受的答案,保证(或至少要求)从 C++11 开始。
我有一个 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;
}
这就是我重现测试所做的——写作 mcve.cc
:
#include <iostream>
int main()
{
for (char i = 0;; ++i) std::cout << (int)i << std::flush;
return 0;
}
我是在VS2013(调试模式)下编译启动的。它开始 "blow" 出数字。
我点击了控制台 window 并且输出停止了(如 OP 所述)。按 ESC 后,我预计会有更多数字,但什么也没发生。
我暂停了调试并查看了调用堆栈,但什么也没有 extra-ordinary。往前走一步,甚至看起来代码还在执行。 (i
的计数仍然发生,正如我在调试器的自动显示中看到的那样。)
所以,我开始应用另一个Q/A
#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 已禁用。 (点击控制台 window 不再停止输出。)
但是,如果没有这个技巧,它应该也能正常工作。 (我不明白这一点很困扰我。) 想了一会儿,我得出了一个启发性的想法。难道 std::cout
在 QuickEdit 之后是 bad()
?
所以,我做了第三个版本。因为我不能使用 cout
put,所以我修改了 i
,我可以在调试器中看到它。 (实际上,std::cout::good()
的 return 也显示了,但分配给 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()
ed:
#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++(没有特定于平台的代码)。
关于 non-Windows 平台的评论会很有趣...
我不记得我曾经在 Linux 上见过这样的 QuickEdit 问题(Irix 或 Solaris 也没有——我过去曾经使用过的操作系统)。在那个系统上,选择由 Xterm/X11 处理(在我的例子中)——超出了流 I/O.
的范围那么,std::cout
是否有可能在那个系统上变坏(假设输出中没有编码错误)?
终于找到了一个可移植的non-invasive方法(代价是multi-threading):
#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
的周期性 check/clean。这是必须添加到 main()
的其他东西(我认为是 "non-invasive fix")——实际代码库不需要更改。
注意:我有点怀疑并发访问 std::cout
是否安全(尽管我相信记得它是安全的)。关于这个,我又找了一个Q/ASO: Is cout synchronized/thread-safe?。根据此 link 中接受的答案,保证(或至少要求)从 C++11 开始。