Skip to content

Instantly share code, notes, and snippets.

@vmrob
Created February 14, 2016 07:56
Show Gist options
  • Save vmrob/ff20420a20c59b5a98a1 to your computer and use it in GitHub Desktop.
Save vmrob/ff20420a20c59b5a98a1 to your computer and use it in GitHub Desktop.
simple nonblocking read from std::cin
#include <iostream>
#include <chrono>
#include <future>
#include <string>
std::string GetLineFromCin() {
std::string line;
std::getline(std::cin, line);
return line;
}
int main() {
auto future = std::async(std::launch::async, GetLineFromCin);
while (true) {
if (future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
auto line = future.get();
// Set a new line. Subtle race condition between the previous line
// and this. Some lines could be missed. To aleviate, you need an
// io-only thread. I'll give an example of that as well.
future = std::async(std::launch::async, GetLineFromCin);
std::cout << "you wrote " << line << std::endl;
}
std::cout << "waiting..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
@bhartsb
Copy link

bhartsb commented Apr 21, 2020

@vmrob "you need an io-only thread. I'll give an example of that as well." Is the example the future = std::async(std::launch::async, GetLineFromCin); or did you intend to add something else?

@vmrob
Copy link
Author

vmrob commented Apr 24, 2020

Well hello everyone! I'm not sure why I wrote this so long ago, but I have a feeling I wrote it intending to show someone a thing and looking at it now, I'm not sure what I was thinking. Since it looks like at least one person has created a stack overflow post leading people here, it's probably worth revisiting.

Today, if I were writing this, it would be probably use a separate thread for io–it's certainly a simpler. It still has efficiency problems related to memory churn, and without any bounding on queue sizes, I think it's sort of flawed, but I guess the context of why this would be desired is pretty necessary to come up with a better solution.

#include <mutex>
#include <deque>
#include <thread>
#include <iostream>
#include <condition_variable>
#include <string>
#include <chrono>

int main() {
    std::condition_variable cv;
    std::mutex mutex;
    std::deque<std::string> lines; // protected by m

    // thread to read from stdin
    std::thread io{[&]{
        std::string tmp;
        while (true) {
            std::getline(std::cin, tmp);
            std::lock_guard lock{mutex};
            lines.push_back(std::move(tmp));
            cv.notify_one();
        }
    }};

    // the nonblocking thread
    std::deque<std::string> toProcess;
    while (true) {
        {
            // critical section
            std::unique_lock lock{mutex};
            if (cv.wait_for(lock, std::chrono::seconds(0), [&]{ return !lines.empty(); })) {
                // get a new batch of lines to process
                std::swap(lines, toProcess);
            }
        }
        if (!toProcess.empty()) {
            std::cout << "processing new lines..." << std::endl;
            for (auto&& line : toProcess) {
                // process lines received by io thread
                std::cout << line << std::endl;
            }
            toProcess.clear();
        }
        std::this_thread::sleep_for(std::chrono::seconds(1));
        std::cout << "waiting 1s..." << std::endl;
    }
}

@danielpetri
Copy link

@vmrob I like this approach. A small notice only: instead of calling sleep at the end of the while loop, why don't you put std::chrono::seconds(1) in the wait_for instead?

@andyflinn
Copy link

@vmrob I like this approach. A small notice only: instead of calling sleep at the end of the while loop, why don't you put std::chrono::seconds(1) in the wait_for instead?

i think he wants mutex (critical condition) to be over as fast as possible, so he doesn't block the io thread

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment