多线程控制台I/O.

Hay*_*ach 8 c++ console multithreading

我在我的多线程应用程序中使用控制台.现在,它只接受输出(printf等),到目前为止我没有任何问题.但是,我希望能够支持控制台输入,这也是我生活变得复杂的地方.

为了预先警告,我对使用控制台输入和输出的更复杂的细微差别非常不熟悉.我在这个主题上的经验并没有比printf/cout,scanf/cin和使用SetConsoleTextAttribute()更改颜色(在窗口上)更进一步.

我宁愿让我的程序尽可能保持交叉兼容,但我不反对必须编写特定于平台的代码,只要我能为其他平台找到可行的替代方案.

从概念上讲,我希望控制台能够运行它自己的线程,这样它就可以在使用cin等待时锁定,而不会冻结整个程序或其他线程.任何线程都可以将控制台输出发送到此线程,该线程将以干净的方式输出(可能使用线程安全队列),并且控制台读取的任何输入都会将命令发送到相应的线程.

我的第一个问题是,当我输入一些输入时,任何输出都会显示在我正在键入的内容中.我想要处理的解决方案是保留控制台的底线以进行输入,并将输出转到第二个最后一行,将输入线向下推.我怎样才能做到这一点?

Hay*_*ach 2

嗯,我用 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.\n\n", APP_MAJORVER, APP_MINORVER, APP_REVISION);
}
Run Code Online (Sandbox Code Playgroud)

接下来,这是负责处理输出的函数。它实际上非常简单,我不需要做任何特殊的事情来保持它的线程安全。我可能根本没有遇到任何问题,但一个简单的解决方法是在其上添加一个互斥体。

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);
}
Run Code Online (Sandbox Code Playgroud)

最后,输入。这需要相当多的微调。

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是从处理窗口消息的同一线程的每一帧运行的。我wgetch()使用 禁用了 的阻塞行为nodelay(),从而无需在其自己的线程中运行控制台输入。我还禁用回显并手动回显输入。在输入窗口上启用滚动允许我使用简单的“\n”清除其内容,如果用户输入了任何内容,则将其替换为更新的内容。它支持简单的多线程终端所期望的一切,能够键入输入以及从多个线程接收输出。