多线程控制台I / O [英] Multithreaded console I/O

查看:240
本文介绍了多线程控制台I / O的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm using a console in my multithreaded application. Right now, it only accepts output (printf and the like) and so far I have no issues. However, I want to be able to support console input as well, and this is where my life gets complicated.

要预警,我非常不熟悉更复杂使用控制台输入和输出的细微差别。我在这个主题的经验不比printf / cout,scanf / cin,并使用 SetConsoleTextAttribute()更改颜色(在Windows上)。

To forewarn, I'm very unfamiliar with the more complicated nuances of working with console input and output. My experience in the subject doesn't go much further than printf/cout, scanf/cin, and using SetConsoleTextAttribute() to change the color (on windows).

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

I would prefer to keep my program as cross-compatible as possible, but I'm not opposed to having to write platform-specific code, as long as I can find viable alternatives for other platforms.

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

Conceptually, I'd like the console to run on it's own thread, so that it can lock up while waiting with cin without freezing the whole program or one of the other threads. Any thread could send console output to this thread which would output it in a clean manner (probably using a thread-safe queue), and any input that the console reads would send the command off to the appropriate thread.

我的第一个问题是,当我输入一些输入,任何输出将显示在我打字的中间。我想要处理的解决方案是保留控制台的底线用于输入,并且输出转到第二最后一行,将输入线向下推。我如何做到这一点?

My first problem is that while I'm typing some input, any output will show up in the middle of what I'm typing. The solution I would like to handle this would be to reserve the bottom line of the console for input, and have output go to the second last line, pushing the input line down. How can I do this?

推荐答案

Wellp,我使用pdcurses解决了。如果有人想做类似的事情,这里是我做的。首先,我初始化控制台这样:

Wellp, I solved it using pdcurses. In case someone else wants to do something similar, here's how I did it. First, I initialize the console thusly:

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);
}

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

Next, This is the function responsible for handling output. It's actually very simple, I don't need to do anything special to keep it thread-safe. I might simply not have encountered any issues with it, but an easy fix would be to slap a mutex on it.

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);
}

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

And finally, input. This one required quite a bit of fine-tuning.

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;
    }
}

窗口消息。我禁用 wgetch()的阻塞行为使用 nodelay(),消除了需要有控制台输入运行自己的线程。我也禁用回声,并手动回传输入。在输入窗口上启用滚动允许我使用简单的\\\
来清除它的内容,如果用户键入了任何内容,则用更新的内容替换它。它支持从一个简单的多线程终端能够输入输入以及从多个线程接收输出的所有东西。

This is run every frame from the same thread that handles window messages. I disabled wgetch()'s blocking behavior using nodelay(), eliminating the need to have console input running in it's own thread. I also disable echoing and echo the input manually. Enabling scrolling on the input window allows me to clear it's contents using a simple "\n", replacing it with updated contents if the user has typed anything. It supports everything one would expect from a simple, multi-threaded terminal capable to typing input as well as receiving output from multiple threads.

这篇关于多线程控制台I / O的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆