У меня есть программа на C ++ (MSVC 2017), которая постоянно выводит отладочную информацию через std :: cout. Однако иногда, когда я физически взаимодействую с консолью (например, случайно нажимаю на нее), она перестает производить вывод. Это означает, что ничего не печатается, хотя программа продолжает работать и завершает делать то, что делает правильно.
Любые идеи, как это исправить? Удаление буфера std :: cout с помощью «std :: cout.setf (std :: ios :: unitbuf);» не имеет никакого эффекта
Образец:
#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 (режим отладки). Это начало «взрывать» числа.
Я щелкнул в окне консоли, и вывод остановился (как описано в ОП). После нажатия ESC, Я ожидал большего числа, но ничего не произошло.
Я приостановил отладку и просмотрел стек вызовов, но не было ничего необычного. Шагнув немного, это даже выглядело так, как будто код все еще выполняется. (Подсчет i
все еще произошло, как я мог видеть на дисплее Auto отладчика.)
Итак, я начал применять решение другого Q / A SO: Как отключить выбор пользователя в консоли Windows. Хотя, похоже, оно того стоило, 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 отключен. (Нажатие в окне консоли больше не прекращало вывод.)
Однако без этого трюка это должно сработать. (Меня беспокоило, что я этого не понял.) Подумав немного, я пришел к просветляющей идее. Может ли быть так, что std::cout
было bad()
после QuickEdit?
Итак, я сделал третью версию. Поскольку я не мог использовать 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()
).Было бы интересно получить замечание по этому поводу не-Windows-платформ …
Я не могу вспомнить, чтобы я когда-либо видел такую проблему QuickEdit в Linux (ни Irix, ни Solaris — операционные системы, которые я использовал однажды в прошлом). В этих системах выборки обрабатывались (в моем случае) Xterm / X11 — за рамками потокового ввода-вывода.
Итак, возможно ли, что 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: Cout синхронизирован / потокобезопасен?. Согласно принятому ответу в этой ссылке, он гарантирован (или, по крайней мере, необходим), начиная с C ++ 11.
Других решений пока нет …