多线程控制台I/O
Multithreaded console I/O
我在多线程应用程序中使用控制台。现在,它只接受输出(printf之类的),到目前为止我还没有遇到任何问题。然而,我也希望能够支持控制台输入,这就是我的生活变得复杂的地方。
事先提醒一下,我对使用控制台输入和输出的更复杂的细微差别非常不熟悉。我在这个主题上的经验并没有比printf/cout, scanf/cin和使用SetConsoleTextAttribute()
来改变颜色(在windows上)更进一步。
我更愿意让我的程序尽可能地交叉兼容,但我不反对必须编写特定于平台的代码,只要我能找到其他平台的可行替代方案。
从概念上讲,我希望控制台在它自己的线程上运行,这样它就可以在等待时锁定,而不会冻结整个程序或其他线程之一。任何线程都可以将控制台输出发送到这个线程,这个线程将以一种干净的方式输出它(可能使用线程安全队列),并且控制台读取的任何输入都会将命令发送到适当的线程。我的第一个问题是,当我输入一些输入时,任何输出都会出现在我输入的内容中间。我想处理这个问题的解决方案是为输入保留控制台的底线,并将输出转到最后第二行,将输入行向下推。我该怎么做呢?
您肯定不希望尝试保留控制台的一部分用于输入,同时写入控制台的其余部分。至少,如果你只是在写滚动文本,就不会这样。这是可能的,但是充满了错误和比它值得的更多的麻烦。有关问题的一些提示,请参阅异步控制台输出。
当然,仅使用conio.h是不可能做到这一点的。
您可以分配两个控制台屏幕缓冲区,一个用于输入,一个用于程序输出。当您的程序正常运行时,输出屏幕缓冲区被选中,您将看到屏幕上滚动的输出。但是,当程序等待用户输入时,您可以交换屏幕缓冲区,以便输出仍然在进行,但是在另一个屏幕缓冲区中。
您最终必须自己格式化输出并调用WriteConsoleOutput,将要写入的屏幕缓冲区的句柄传递给它。它会在很短的时间内变得很复杂,而且很难做到正确。如果有可能的话。我知道我过去在这上面花了太多时间,而且总是有奇怪的问题。
我不会说你想做的事是不可能的。然而,我要说的是,你将会有一段艰难的时光。好吧,我用pdcurses解决了这个问题。如果有人想做类似的事情,我是这样做的。首先,我这样初始化控制台:
Console::Console(bool makeConsole)
{
if (makeConsole == false)
return;
if (self)
throw ("You only need one console - do not make another!n");
self = this;
#ifdef WIN32
AllocConsole();
#endif
initscr();
inputLine = newwin(1, COLS, LINES - 1, 0);
outputLines = newwin(LINES - 1, COLS, 0, 0);
if (has_colors())
{
start_color();
for (int i = 1; i <= COLOR_WHITE; ++i)
{
init_pair(i, i, COLOR_BLACK);
}
}
else
wprintw(outputLines, "Terminal cannot print colors.n");
scrollok(outputLines, TRUE);
scrollok(inputLine, TRUE);
leaveok(inputLine, TRUE);
nodelay(inputLine, TRUE);
cbreak();
noecho();
keypad(inputLine, TRUE);
initCommands();
hello("Starting %s.n", APP_NAME);
hellomore("Version %i.%i.%i.nn", APP_MAJORVER, APP_MINORVER, APP_REVISION);
}
接下来,这是负责处理输出的函数。它实际上非常简单,我不需要做任何特别的事情来保持线程安全。我可能根本没有遇到任何问题,但一个简单的解决办法是在它上面加一个互斥锁。
void Console::sendFormattedMsg(short prefixColor, const char* prefix, short color, const char* format, ...)
{
if (!self)
return;
va_list args;
va_start(args, format);
if (has_colors())
{
if (prefix)
{
wattron(outputLines, A_BOLD | COLOR_PAIR(prefixColor));
wprintw(outputLines, prefix);
}
if (color == COLOR_WHITE)
wattroff(outputLines, A_BOLD);
wattron(outputLines, COLOR_PAIR(color));
vwprintw(outputLines, format, args);
wattroff(outputLines, A_BOLD | COLOR_PAIR(color));
}
else
{
wprintw(outputLines, prefix);
vwprintw(outputLines, format, args);
}
wrefresh(outputLines);
va_end(args);
}
最后,输入。这个需要相当多的微调。
void Console::inputLoop(void)
{
static string input;
wattron(inputLine, A_BOLD | COLOR_PAIR(COLOR_WHITE));
wprintw(inputLine, "n> ");
wattroff(inputLine, A_BOLD | COLOR_PAIR(COLOR_WHITE));
wprintw(inputLine, input.c_str());
wrefresh(inputLine);
char c = wgetch(inputLine);
if (c == ERR)
return;
switch (c)
{
case 'n':
if (input.size() > 0)
{
sendFormattedMsg(COLOR_WHITE, "> ", COLOR_WHITE, input.c_str());
cprint("n");
executeCommand(&input[0]);
input.clear();
}
break;
case 8:
case 127:
if (input.size() > 0) input.pop_back();
break;
default:
input += c;
break;
}
}
这是从处理窗口消息的同一个线程中运行的每一帧。我使用nodelay()
禁用wgetch()
的阻塞行为,消除了在自己的线程中运行控制台输入的需要。我还禁用了回显并手动回显输入。在输入窗口上启用滚动允许我使用简单的"n"清除它的内容,如果用户输入了任何内容,则将其替换为更新的内容。它支持一个简单的多线程终端,能够输入和接收多个线程的输出。
要禁用回显字符,请查看以下内容:从std::cin
读取密码或许可以结合这家伙关于Win32 console io的博文。
你可能也会发现这些东西很有用:conio.h,pdcurses
- 在C++中使用cURL和多线程
- 多线程双缓冲区
- 为什么我的多线程作业队列崩溃
- 在main()之外初始化std::vector会导致性能下降(多线程)
- 试图创建一个多线程程序来查找0-100000000之间的总素数
- 为什么一个向量上的多线程操作很慢
- 学习多线程C++:添加线程不会使执行速度更快,即使它看起来应该
- 全局变量 多读取器 一个写入器多线程安全?
- boost::文件系统::recursive_directory_iterator多线程安全
- 如何阻止TensorFlow的多线程
- 如何在多线程中正确使用unique_ptr进行多态性?
- 并发/多线程:是否可以以这种方式生成相同的输出?
- sigwait() 在多线程程序中不起作用
- 多线程程序中出现意外的内存泄漏
- 带有 std::cout 的多线程控制台文本动画
- 如何在多线程 MEX 函数中打印到控制台?
- 控制台C++中的多线程定时器和I/O
- 如何制作具有多线程的简单Qt控制台应用程序
- 多线程文本输出打印到dos控制台重叠/混乱输出
- 多线程控制台I/O