webentwicklung-frage-antwort-db.com.de

Nicht blockierende Konsoleneingabe C++

Ich suche nach einer (Multiplattform-) Möglichkeit, eine nicht blockierende Konsoleneingabe für mein C++ - Programm auszuführen, sodass ich Benutzerbefehle behandeln kann, während das Programm kontinuierlich ausgeführt wird. Das Programm gibt gleichzeitig auch Informationen aus. 

Was ist der beste/einfachste Weg, dies zu tun? Ich habe kein Problem damit, externe Bibliotheken wie boost zu verwenden, solange sie eine Lizenz verwenden.

30
Doug

Ich würde dies tun, indem ich einen separaten Thread erstellt, der normale Blockierungsfunktionen IO aufruft, und eine Callback-Funktion übergeben, die er aufrufen würde, wenn er eine Eingabe erhalten würde. Bist du sicher, dass du das tun musst, was du gesagt hast?

Was würde passieren, wenn Informationen gleichzeitig ausgegeben werden, wenn der Benutzer gerade Eingaben getippt und etwas gedruckt hat?

9
Seth Carnegie

Ich habe dies auf QNX4.5 gemacht, das Threads oder Boost nicht unterstützt, indem select verwendet wird. Sie übergeben im Wesentlichen select STDIN als zu verwendenden Dateideskriptor und select wird zurückgegeben, wenn eine neue Zeile eingegeben wird. Ich habe unten eine vereinfachte Beispielschleife hinzugefügt. Es ist plattformunabhängig, zumindest für Unix-ähnliche Systeme. Nicht sicher über Windows.

while (!g_quit)
{
   //we want to receive data from stdin so add these file
   //descriptors to the file descriptor set. These also have to be reset
   //within the loop since select modifies the sets.
   FD_ZERO(&read_fds);
   FD_SET(STDIN_FILENO, &read_fds);

   result = select(sfd + 1, &read_fds, NULL, NULL, NULL);
   if (result == -1 && errno != EINTR)
   {
      cerr << "Error in select: " << strerror(errno) << "\n";
      break;
   }
   else if (result == -1 && errno == EINTR)
   {
      //we've received and interrupt - handle this
      ....
   }
   else
   {
      if (FD_ISSET(STDIN_FILENO, &read_fds))
      {
         process_cmd(sfd);
      }
   }
}
4
sashang

Es gibt einen einfachen Weg:

char buffer[512];
int point = 0;
...
while (_kbhit()) {
    char cur = _getch();
    if (point > 511) point = 511;
    std::cout << cur;
    if (cur != 13) buffer[point++] = cur;
    else{
        buffer[point] = '\0';
        point = 0;
        //Run(buffer);
    }
}

Kein Block, alles in einem Thread. Was mich betrifft, das funktioniert.

4
Rihard S

Nicht blockierende Konsoleneingabe C++?

Antwort: Konsultieren Sie IO in einem Hintergrund-Thread und stellen Sie ein Mittel zur Kommunikation zwischen Threads bereit.

Hier ist ein komplettes (aber einfaches) Testprogramm, das async io implementiert, indem das io auf einen Hintergrundthread verschoben wird.

das Programm wartet darauf, dass Sie Zeichenfolgen auf der Konsole eingeben (mit newline beenden) und dann eine 10-Sekunden-Operation mit dieser Zeichenfolge ausführen.

sie können während des Vorgangs eine andere Zeichenfolge eingeben.

geben Sie 'quit' ein, damit das Programm beim nächsten Zyklus angehalten wird.

#include <iostream>
#include <memory>
#include <string>
#include <future>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <deque>

int main()
{
    std::mutex m;
    std::condition_variable cv;
    std::string new_string;
    bool error = false;

    auto io_thread = std::thread([&]{
        std::string s;
        while(!error && std::getline(std::cin, s, '\n'))
        {
            auto lock = std::unique_lock<std::mutex>(m);
            new_string = std::move(s);
            if (new_string == "quit") {
                error = true;
            }
            lock.unlock();
            cv.notify_all();
        }
        auto lock = std::unique_lock<std::mutex>(m);
        error = true;
        lock.unlock();
        cv.notify_all();
    });

    auto current_string = std::string();
    for ( ;; )
    {
        auto lock = std::unique_lock<std::mutex>(m);
        cv.wait(lock, [&] { return error || (current_string != new_string); });
        if (error)
        {
            break;
        }
        current_string = new_string;
        lock.unlock();

        // now use the string that arrived from our non-blocking stream
        std::cout << "new string: " << current_string;
        std::cout.flush();
        for (int i = 0 ; i < 10 ; ++i) {
            std::this_thread::sleep_for(std::chrono::seconds(1));
            std::cout << " " << i;
            std::cout.flush();
        }
        std::cout << ". done. next?\n";
        std::cout.flush();
    }
    io_thread.join();
    return 0;
}

beispieltestlauf:

$ ./async.cpp
first
new string: first 0 1las 2t 3
 4 5 6 7 8 9. done. next?
new string: last 0 1 2 3 4 5 6 7 8quit 9. done. next?
3
Richard Hodges

Beispiel mit C++ 11:

#include <iostream>
#include <future>
#include <thread>
#include <chrono>

static std::string getAnswer()
{    
    std::string answer;
    std::cin >> answer;
    return answer;
}

int main()
{
    int timeout = 5;
    std::cout << "do you even lift?" << std::endl;
    std::string answer = "maybe"; //default to maybe
    std::future<std::string> future = std::async(getAnswer);
    if (future.wait_for(std::chrono::seconds(timeout)) == std::future_status::ready)
        answer = future.get();

    std::cout << "the answer was: " << answer << std::endl;
    exit(0);
}

online-Compiler: http://rextester.com/XGX58614

2
Sascha

ncurses kann ein guter Kandidat sein.

2
Simon

Die Klasse StdinDataIO der BSD-lizenzierten MUSCLE-Netzwerkbibliothek unterstützt nicht blockierende Lesevorgänge von stdin unter Windows, MacOS/X und Linux/Unix als ein Beispiel dafür, wie es gemacht werden kann), wenn Sie möchten.

0
Jeremy Friesner

Sie können dazu die tinycon-Bibliothek verwenden. Spawn einfach ein tinycon-Objekt in einem neuen Thread, und du bist ziemlich fertig. Sie können die Auslösemethode definieren, die ausgelöst wird, wenn Sie die Eingabetaste drücken.

Sie finden es hier: https://sourceforge.net/projects/tinycon/

Die Lizenz ist auch BSD, also wird sie für Ihre Anforderungen am meisten freigegeben.

0
Unix-Ninja

libuv ist eine plattformübergreifende C-Bibliothek für asynchrone E/A. Es verwendet eine Ereignisschleife, um beispielsweise die Standardeingabe zu lesen, ohne den Thread zu blockieren. libuv ist das, was Node.JS und andere antreibt.

0
sffc