关于c ++:non-blocking std :: getline,如果没有输入则退出

non-blocking std::getline, exit if no input

当前,我有一个程序从标准输入中读取内容,偶尔如果没有输入,该程序只需要保持运行即可,通常这是一个测试脚本,可以说没有"输入"。

program -v1 -v2 -v3 output

v1-v3分别是命令行参数

基本上,如果未提供"输入",则程序将命令行参数及其各自的含义吐给程序,然后退出。

但是目前,如果给它一个空的测试文件,或者在std :: getline上运行它后在不按回车键的情况下运行它,我就用来输入命令。

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

其中foo是一个字符串。

我如何让它至少连续运行一次并do stuff然后在没有输入的情况下退出?在输入的情况下,do stuff在标准输入中每行出现一次。

切换到do-while循环,并检查pre循环是否有任何输入是否有效?

就像是

1
2
3
4
5
6
7
if cin empty
set flag

do
 {do stuff
 check flag}
while(getline)

还是在C ++中不阻塞io是不可能的?

这个问题似乎一遍又一遍地重复了,但是我找不到一个明确的答案,甚至找不到与平台无关的答案(该程序本质上是学术性的,在Windows上进行编码,并在Unix上进行了测试)。


异步使用std :: cin可能是完成此工作的唯一方法,因为iostream并非设计为具有非阻塞行为。这是一个例子:

Async Example 1
Async Example 2 (printing a space out every 1/10th of a second while accepting CLI input at the same time)

该代码带有注释,因此应该易于理解。这是一个线程安全的类,可让您使用std :: cin异步获取行。

非常容易用于异步CLI getline,我的计算机上的CPU使用率为0%。它在Windows 10上的Visual Studio 2015 c ++ Win32控制台调试和发布模式下运行良好。如果它在您的OS或环境中不起作用,那就太糟糕了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include

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 == '\
'
)
                        {
                            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 <<"}\
"
;

            //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 <<"\
\
"
;
    system("pause");
}


您可以使用cin.peek检查是否有任何要读取的内容,然后调用getline。但是,它本身并不存在非阻塞性的getline。


您可以使用istream :: readsome()方法相当容易地使非阻塞等效于std :: getline。这将读取最大可用缓冲区大小的可用输入,而不会阻塞。

此函数将始终立即返回,但如果流中有一行,则会捕获一行。分行存储在静态变量中,直到下一次调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
bool getline_async(std::istream& is, std::string& str, char delim = '\
'
) {

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