如何使用C++future在不阻塞的情况下读取stdin

How to read stdin without blocking using C++ future?

本文关键字:情况下 读取 stdin 何使用 C++future      更新时间:2024-03-29

测试程序StdoutWriter在1秒内将一些文本({"id":0,"cmd":1}(写入stdout,然后在5秒后再次写入,然后等待10秒并退出。我自己运行了这个程序,并验证了时间和输出是否正确。

另一个测试程序StdinReader(下面给出的代码(使用future读取stdin并打印每一行StdinReader未按预期工作。

我从终端启动两个程序:

./StdoutWriter | ./StdinReader

我看到的问题是,在StdoutWriter退出之前,每一行的读取似乎都会被阻塞。

观察到的错误输出看起来像:

waiting...
waiting...
waiting...
waiting...
waiting...
waiting...
waiting...
waiting...
waiting...
waiting...
you wrote {"id":0,"cmd":1}
waiting...
you wrote {"id":0,"cmd":1}
waiting...
you wrote 
waiting...
you wrote 
waiting...
you wrote 

正确输出应为:

waiting...
waiting...
you wrote {"id":0,"cmd":1}
waiting...
waiting...
waiting...
waiting...
waiting...
you wrote {"id":0,"cmd":1}
waiting...
waiting...
waiting...
waiting...
you wrote 
waiting...
you wrote 
waiting...
you wrote

这是我从StdinReader获得的代码:https://gist.github.com/vmrob/ff20420a20c59b5a98a1

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

您的StdoutWriter程序实际上可能并没有像您想象的那样写入stdout。如果您在写入后显式刷新输出流,那么您的StdinReader很可能会像您预期的那样工作。

为了提高性能,大多数I/O库在写入输出流时都会进行一定程度的缓冲。这意味着,当您写入输出流时,库通常只在内部保留数据,而不是直接将其写入其底层操作系统文件描述符。这避免了为每次写入都执行系统调用而付出代价。

缓冲级别通常会根据您要写入的文件/设备的类型而变化:

  • 在向终端写入时,输出通常是行缓冲的。这意味着库将刷新其内部缓冲区,并在每次写入换行符时将输出写入底层文件描述符。

  • 当写入管道或普通文件时,输出通常是完全缓冲的。这意味着,当缓冲区填满或您明确告诉它时,库只会将数据从其内部缓冲区刷新到底层文件描述符

相关文章: