non-blocking std::getline, exit if no input
当前,我有一个程序从标准输入中读取内容,偶尔如果没有输入,该程序只需要保持运行即可,通常这是一个测试脚本,可以说没有"输入"。
v1-v3分别是命令行参数
基本上,如果未提供"输入",则程序将命令行参数及其各自的含义吐给程序,然后退出。
但是目前,如果给它一个空的测试文件,或者在std :: getline上运行它后在不按回车键的情况下运行它,我就用来输入命令。
1 2 | while(std::getline(std::cin,foo) {do stuff} |
其中foo是一个字符串。
我如何让它至少连续运行一次并
切换到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并非设计为具有非阻塞行为。这是一个例子:
该代码带有注释,因此应该易于理解。这是一个线程安全的类,可让您使用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"); } |
您可以使用
您可以使用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; } |