【问题标题】:Simultaneous input and output in a console在控制台中同时输入和输出
【发布时间】:2019-08-20 04:56:11
【问题描述】:

将值无限打印到控制台的线程和从控制台获取用户输入的主线程,但输入值与该线程的输出混合。

C++ : cin while cout

确实给了我关于如何进一步前进的提示,但我无法提出自己的解决方案(因为我是 c++ 新手)。

using namespace std;

mutex mtx;

void foo()
{
    while (1)
    {
        usleep(1000000);
        cout << "Cake\n";
    }
}

int main()
{
    mtx.lock();
    thread t1(foo);
    string x;
    while (true)
    {
        cin >> x;
        edit//fflush(stdin);
        cout << x << "\n";
    }
    t1.join();
    mtx.unlock();
    return 0;
}

编辑 1:

好吧,更准确地说,我真正想要的是,

IN 终端(目前

output:cake (which prints every second)
output:cake 
output:cake 
output:cake 
input:hi
output:hicake (still yet to give the enter it echo's the input to console)
output:cake 

我在终端中真正想要的是输入独立于输出

output:cake 
output:cake 
output:cake 
input:hi
output:cake 
output:cake 
input:hi(waiting still for enter)

//and when enter is pressed it should print to the console
output:hi
output:cake

注意:禁用回声没有帮助。

编辑 2: 我发布的答案是数据处理,其中并发操作在给定命令上停止。

【问题讨论】:

  • 你在一段时间之前锁定了你的互斥锁,然后在一段时间内解锁......你应该解锁/锁定相同的次数
  • 对不起,我现在已经编辑了代码,但即使在那之后我也无法获得所需的解决方案
  • fflush(stdin); 是未定义的行为,没有任何意义。永远不要那样做。仅在一个线程中使用的互斥锁没有意义。您需要围绕每个线程中对资源的每次访问锁定互斥锁。
  • 感谢您的更正,我会牢记这一点@n.m

标签: c++ linux multithreading


【解决方案1】:

能够使用所有给定的输入提出解决方案,我希望这对某人有所帮助。

#include <termios.h>
#include <unistd.h>
#include <stdio.h>
#include <cstring>
#include <mutex>
#include <string>
#include <chrono>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <thread>
/// reads a character from console without echo.
int getChar()
{
    struct termios oldattr;
    tcgetattr(STDIN_FILENO, &oldattr);
    struct termios newattr = oldattr;
    newattr.c_lflag &= ~(ICANON | ECHO);
    tcsetattr(STDIN_FILENO, TCSANOW, &newattr);
    const int ch = getchar();
    tcsetattr(STDIN_FILENO, TCSANOW, &oldattr);
    return ch;
}

class Console
{
  private:
    // mutex for console I/O
    std::mutex _mtx;
    // current input
    std::string _input;
    // prompt output
    std::string _prompt;

  public:
    Console() {}

    Console(const Console &) = delete;
    Console &operator=(const Console &) = delete;

    std::string read();

    void write(const char *text, size_t size);
    void write(const char *text) { write(text, strlen(text)); }
    void write(const std::string &text) { write(text.c_str(), text.size()); }
};

std::string Console::read()
{
    { // activate prompt
        std::lock_guard<std::mutex> lock(_mtx);
        _prompt = "> ";
        _input.clear();
        std::cout << _prompt << std::flush;
    }
    enum
    {
        Enter = '\n',
        BackSpc = 127
    };
    for (;;)
    {
        switch (int c = getChar())
        {
        case Enter:
        {
            std::lock_guard<std::mutex> lock(_mtx);
            std::string input = _input;
            _prompt.clear();
            _input.clear();
            std::cout << std::endl;
            return input;
        } // unreachable: break;
        case BackSpc:
        {
            std::lock_guard<std::mutex> lock(_mtx);
            if (_input.empty())
                break;
            _input.pop_back();
            std::cout << "\b \b" << std::flush;
        }
        break;
        default:
        {
            if (c < ' ' || c >= '\x7f')
                break;
            std::lock_guard<std::mutex> lock(_mtx);
            _input += c;
            std::cout << (char)c << std::flush;
        }
        break;
        }
    }
}

void Console::write(const char *text, size_t len)
{
    if (!len)
        return;
    bool eol = text[len - 1] == '\n';
    std::lock_guard<std::mutex> lock(_mtx);
    // remove current input echo
    if (size_t size = _prompt.size() + _input.size())
    {
        std::cout
            << std::setfill('\b') << std::setw(size) << ""
            << std::setfill(' ') << std::setw(size) << ""
            << std::setfill('\b') << std::setw(size) << "";
    }
    // print text
    std::cout << text;
    if (!eol)
        std::cout << std::endl;
    // print current input echo
    std::cout << _prompt << _input << std::flush;
}

struct Flags //this flag is shared between both the threads
{
    // flag: true then exit communication thread and main loop
    bool exit;
    // flag: true then start data processing
    bool start;
    // the mini console
    Console console;

    // constructor.
    Flags() : exit(false), start(true) {}
};

void dataProc(Flags &shared)
{
    int i = 0;
    while (!shared.exit)
    {
        while (!shared.start)
        {
            if (shared.exit)
                return;
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
        }
        shared.console.write("Starting data processing.");
        for (;;)
        {
            if (!shared.start || shared.exit)
            {

                shared.console.write("Data processing stopped.");
                while (!shared.start || shared.exit)
                {
                    if (shared.exit)
                        return;
                    std::this_thread::sleep_for(std::chrono::milliseconds(100));
                }
                shared.console.write("Data processing restarted.");
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(250));
            {
                std::ostringstream fmt;
                fmt << "Cake " << ++i;
                shared.console.write(fmt.str());
            }
        }
        shared.console.write("Data processing done.");
        shared.start = false;
    }
}

void processInput(const std::string &input, Flags &shared)
{

    if (strcasecmp(input.c_str(),"start")==0)
        shared.start = true;
    else if (strcasecmp(input.c_str(),"stop")==0)
        shared.start = false;
    else if (strcasecmp(input.c_str(),"exit")==0)
        shared.exit = true;
    else if (input.size())
        shared.console.write("Wrong command!");
}




int main()
{
    Flags shared;
    std::thread threadProc(&dataProc, std::ref(shared));

    while (!shared.exit)
    {
        shared.console.write("Commands accepted: start stop exit");
        std::string input = shared.console.read();
        processInput(input, shared);
    }
    threadProc.join();
    return 0;
}

【讨论】:

  • 太好了,你实际上可以用它来制作像 Vim 这样的终端文本编辑器。我从本教程中学到了这一点:Build Your Own Editor
【解决方案2】:

我建议将mtx 锁定为cout 以及cin

using namespace std;

mutex mtx;

void foo()
{
    while (1)
    {
        usleep(1000000);
        mtx.lock();
        cout << "Cake\n";
        mtx.unlock();
    }
}

int main()
{
    thread t1(foo);
    string x;
    while (true)
    {
        cin >> x;
        mtx.lock();
        cout << x << "\n";
        mtx.unlock();
    }
    t1.join();
    return 0;
}

【讨论】: