Does non-blocking read from std::cin work with std

2019-02-28 12:17发布

I've encountered a specific problem with my implementation and can't find a solution for it.

I have a two-part application. One part is a Java swing GUI. The second part is a C++ application that does all the (time consuming) calculation logic. The two processes communicate (in both directions) with their output and input streams. My problem now is that in one part of the C++ program I have to wait for user input coming from the Java program. However, waiting seems to block.

What works perfectly when I call the program in a shell is:

std::string inputLine1;
std::cin >> inputLine1;

When working with the Java UI, this does not work (naturally), because reading from std::cin is blocking, so when the C++ application waits for input, the Java application can't do anything.

Therefore I made another way of reading from std::cin that should (at least in my mind) work, but I can't make it work. It is:

std::string inputLine1;
while (true)
{
    int c = std::cin.peek();
    if (c != EOF)
    {
        std::cin >> inputLine1;
        break;
    }
    std::this_thread::yield();
}

I also tried to replace the line with yield() with

std::this_thread::sleep_for(std::chrono::milliseconds(500));

In my mind, this code should work as following: I peek on std::cin. If something is there, I read it from cin. If nothing is there, I yield and try again later.

I know, yielding is considered to not be a very clean way of working, but I want to keep the communication between those two applications as simple as possible. No third party libraries, no more complex concepts (as Sockets), if possible.

However, this approach doesn't work, it gives the same behaviour as the first approach with just reading in from std::cin. The Java program becomes unresponsive and none of the two applications seem to do anything.

The C++ application works perfectly if called in a shell and if I provide the same input from the keyboard, so the problem shouldn't be there. If I delete all these given code snippets from the C++ application, the Java application is responsive and works - although it doesn't get the input it needs, obviously.

1条回答
女痞
2楼-- · 2019-02-28 13:07

After trying for a long time to implement non-blocking input from cin, I'm pretty sure it's impossible to getting it to work consistently.

My current solution is to put the blocking cin into it's own tiny thread and let it do it's thing.

I've simplified my implementation a little bit for this example, as you need a thread safe storage system of some kind.

#include <iostream>
#include <thread>
#include <mutex>
#include <queue>

// Super simple thread safe storage
std::queue<std::string> Database;
std::mutex Padlock;
void PushLine(std::string Line) {
    std::unique_lock<std::mutex> Lock(Padlock); (void)Lock;
    Database.push(Line);
}
bool IsLineAvailable(void) {
    std::unique_lock<std::mutex> Lock(Padlock); (void)Lock;
    return !Database.empty();
}
std::string PopLine(void) {
    std::unique_lock<std::mutex> Lock(Padlock); (void)Lock;
    std::string Line(std::move(Database.front()));
    Database.pop();
    return Line;
}

// Main function with "non-blocking" input from cin
int main(int argc, char *argv[]) {
    (void)argc;
    (void)argv;
    std::thread InputThread = std::thread([](){
        do {
            // Ensure the input is as clean as possible
            if (std::cin.rdbuf()->in_avail()) {
                std::cin.ignore(std::cin.rdbuf()->in_avail());
            }
            std::cin.clear();

            // Get a line, cin will block here.
            std::string Line;
            std::getline(std::cin, Line);

            // If the line is not empty attempt to store it.
            if (!Line.empty()) {
                PushLine(Line);
            }
        } while (1);
    });

    // Detach from the thread, it will never end.
    InputThread.detach();

    // A job to do.
    unsigned int Counter = 0;

    // Run your program.
    bool Running = true;
    while(Running) {
        // Perform a job, in this case counting.
        Counter++;

        // Check for available input
        if (IsLineAvailable()) {
            // If there is input available, first get it
            std::string Line = PopLine();

            // Echo it to the terminal
            std::cout << "Command: " << Line << std::endl;

            // Perform actions based on the command
            if (Line == "quit") {
                Running = false;
            }
            else if (Line == "count") {
                std::cout << "  Count: " << Counter << std::endl;
            }
        }

        // Sleep for a while
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }

    // Done.
    return 0;
}
查看更多
登录 后发表回答