简体   繁体   English

非阻塞std :: getline,如果没有输入则退出

[英]non-blocking std::getline, exit if no input

Currently I have a program that reads from the standard input, occasionally the program needs to just keep running if no input is made, usually this is a test script there is no 'enter' so to speak. 目前我有一个从标准输入读取的程序,如果没有输入,程序偶尔需要继续运行,通常这是一个测试脚本没有'输入'可以这么说。

program -v1 -v2 -v3 <input >output

v1 - v3 are command line arguments respectively v1 - v3分别是命令行参数

Basically the program spits out the command line arguments and their respective meaning to the program if no 'input' is given and then should exit. 基本上,如果没有给出“输入”,程序会将命令行参数及其各自的含义吐出,然后退出。

However at the moment if give it an empty test file or just run without pressing enter after running it blocks on the std::getline I use to input the commands. 但是目前如果给它一个空的测试文件,或者只是在运行std :: getline上的块之后按下输入,我用来输入命令。

while(std::getline(std::cin,foo)
{do stuff}

where foo is a string. 其中foo是一个字符串。

How do I get it to just run through and do stuff at least once then exit in the event of no input? 我如何让它只是运行并至少do stuff一次然后在没有输入的情况下退出? In the event of input the do stuff occurs once for every line in standard input. 在输入的情况下,对于标准输入中的每一行, do stuff发生一次。

Would a switch to a do-while loop, with a check pre loop as to whether it's got any input, work? 是否可以切换到do-while循环,并检查预循环是否有任何输入?

Something like 就像是

if cin empty
set flag

do
 {do stuff
 check flag}
while(getline)

or is non-blocking io not possible in c++? 或者是非阻塞的,在c ++中是不可能的?

This question seems to be rehashed over and over but I couldn't find a definitive answer or even an answer that was platform agnostic(this program is academic in nature, coded on windows and tested on unix). 这个问题似乎反复重复,但我找不到一个明确的答案,甚至是一个平台无关的答案(这个程序本质上是学术性的,在windows上编码并在unix上测试)。

Using std::cin asynchronously might be the only way to make this work, as iostream is not designed to have non-blocking behavior. 异步使用std :: cin可能是使其工作的唯一方法,因为iostream并非设计为具有非阻塞行为。 Here is an example: 这是一个例子:

异步示例1 异步示例2(每隔1/10秒打印一个空格,同时接受CLI输入)

The code is commented so it should be easy to understand. 代码已注释,因此应易于理解。 It's a thread-safe class that lets you asynchronously get a line using std::cin. 它是一个线程安全的类,允许您使用std :: cin异步获取一行。

Very easy to use for asynchronous CLI getline purposes, with 0% CPU usage on my computer. 非常容易用于异步CLI getline,在我的计算机上占用0%的CPU。 It works perfectly well on Windows 10 in Visual Studio 2015 c++ Win32 Console Debug and Release mode. 它在Visual Studio 2015 c ++ Win32控制台调试和发布模式下在Windows 10上运行良好。 If it doesn't work in your OS or environment, that's too bad. 如果它在您的操作系统或环境中不起作用,那就太糟糕了。

#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <atomic>

using namespace std;

//This code works perfectly well on Windows 10 in Visual Studio 2015 c++ Win32 Console Debug and Release mode.
//If it doesn't work in your OS or environment, that's too bad; guess you'll have to fix it. :(
//You are free to use this code however you please, with one exception: no plagiarism!
//(You can include this in a much bigger project without giving any credit.)
class AsyncGetline
{
    public:
        //AsyncGetline is a class that allows for asynchronous CLI getline-style input
        //(with 0% CPU usage!), which normal iostream usage does not easily allow.
        AsyncGetline()
        {
            input = "";
            sendOverNextLine = true;
            continueGettingInput = true;

            //Start a new detached thread to call getline over and over again and retrieve new input to be processed.
            thread([&]()
            {
                //Non-synchronized string of input for the getline calls.
                string synchronousInput;
                char nextCharacter;

                //Get the asynchronous input lines.
                do
                {
                    //Start with an empty line.
                    synchronousInput = "";

                    //Process input characters one at a time asynchronously, until a new line character is reached.
                    while (continueGettingInput)
                    {
                        //See if there are any input characters available (asynchronously).
                        while (cin.peek() == EOF)
                        {
                            //Ensure that the other thread is always yielded to when necessary. Don't sleep here;
                            //only yield, in order to ensure that processing will be as responsive as possible.
                            this_thread::yield();
                        }

                        //Get the next character that is known to be available.
                        nextCharacter = cin.get();

                        //Check for new line character.
                        if (nextCharacter == '\n')
                        {
                            break;
                        }

                        //Since this character is not a new line character, add it to the synchronousInput string.
                        synchronousInput += nextCharacter;
                    }

                    //Be ready to stop retrieving input at any moment.
                    if (!continueGettingInput)
                    {
                        break;
                    }

                    //Wait until the processing thread is ready to process the next line.
                    while (continueGettingInput && !sendOverNextLine)
                    {
                        //Ensure that the other thread is always yielded to when necessary. Don't sleep here;
                        //only yield, in order to ensure that the processing will be as responsive as possible.
                        this_thread::yield();
                    }

                    //Be ready to stop retrieving input at any moment.
                    if (!continueGettingInput)
                    {
                        break;
                    }

                    //Safely send the next line of input over for usage in the processing thread.
                    inputLock.lock();
                    input = synchronousInput;
                    inputLock.unlock();

                    //Signal that although this thread will read in the next line,
                    //it will not send it over until the processing thread is ready.
                    sendOverNextLine = false;
                }
                while (continueGettingInput && input != "exit");
            }).detach();
        }

        //Stop getting asynchronous CLI input.
        ~AsyncGetline()
        {
            //Stop the getline thread.
            continueGettingInput = false;
        }

        //Get the next line of input if there is any; if not, sleep for a millisecond and return an empty string.
        string GetLine()
        {
            //See if the next line of input, if any, is ready to be processed.
            if (sendOverNextLine)
            {
                //Don't consume the CPU while waiting for input; this_thread::yield()
                //would still consume a lot of CPU, so sleep must be used.
                this_thread::sleep_for(chrono::milliseconds(1));

                return "";
            }
            else
            {
                //Retrieve the next line of input from the getline thread and store it for return.
                inputLock.lock();
                string returnInput = input;
                inputLock.unlock();

                //Also, signal to the getline thread that it can continue
                //sending over the next line of input, if available.
                sendOverNextLine = true;

                return returnInput;
            }
        }

    private:
        //Cross-thread-safe boolean to tell the getline thread to stop when AsyncGetline is deconstructed.
        atomic<bool> continueGettingInput;

        //Cross-thread-safe boolean to denote when the processing thread is ready for the next input line.
        //This exists to prevent any previous line(s) from being overwritten by new input lines without
        //using a queue by only processing further getline input when the processing thread is ready.
        atomic<bool> sendOverNextLine;

        //Mutex lock to ensure only one thread (processing vs. getline) is accessing the input string at a time.
        mutex inputLock;

        //string utilized safely by each thread due to the inputLock mutex.
        string input;
};

void main()
{
    AsyncGetline ag;
    string input;

    while (true)
    {
        //Asynchronously get the next line of input, if any. This function automagically
        //sleeps a millisecond if there is no getline input.
        input = ag.GetLine();

        //Check to see if there was any input.
        if (!input.empty())
        {
            //Print out the user's input to demonstrate it being processed.
            cout << "{" << input << "}\n";

            //Check for the exit condition.
            if (input == "exit")
            {
                break;
            }
        }

        //Print out a space character every so often to demonstrate asynchronicity.
        //cout << " ";
        //this_thread::sleep_for(chrono::milliseconds(100));
    }

    cout << "\n\n";
    system("pause");
}

You can use cin.peek to check if there is anything to read, and then call getline if there is. 您可以使用cin.peek检查是否有任何要读取的内容,然后调用getline如果有)。 There's no such thing as non-blocking getline by itself though. 尽管如此,没有非阻塞getline这样的东西。

You can make a non-blocking equivalent to std::getline fairly easily using the istream::readsome() method. 您可以使用istream :: readsome()方法相当容易地创建与std :: getline等效的非阻塞。 This reads available input up to a maximum buffer size, without blocking. 这将读取最大缓冲区大小的可用输入,而不会阻塞。

This function will always return instantly, but will capture a line if one is available on the stream. 此函数将始终立即返回,但如果流上有可用的行,则会捕获一行。 Partial lines are stored in a static variable until the next call. 部分行存储在静态变量中,直到下一次调用。

bool getline_async(std::istream& is, std::string& str, char delim = '\n') {

    static std::string lineSoFar;
    char inChar;
    int charsRead = 0;
    bool lineRead = false;
    str = "";

    do {
        charsRead = is.readsome(&inChar, 1);
        if (charsRead == 1) {
            // if the delimiter is read then return the string so far
            if (inChar == delim) {
                str = lineSoFar;
                lineSoFar = "";
                lineRead = true;
            } else {  // otherwise add it to the string so far
                lineSoFar.append(1, inChar);
            }
        }
    } while (charsRead != 0 && !lineRead);

    return lineRead;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM