无需等待输入即可输入?
Posted
技术标签:
【中文标题】无需等待输入即可输入?【英文标题】:Cin without waiting for input? 【发布时间】:2015-02-19 08:38:42 【问题描述】:对于我正在进行的项目,我需要程序能够接收用户的输入,但是当他们输入内容时,程序可以继续循环。
例如:while (true)
if (userInput == true)
cin >> input
//DO SOMETHING
这意味着//DO SOMETHING
将在每个循环中发生,而无需用户按回车一百万次。
kbhit()
和 getch()
创建自己的输入,但这变得非常混乱,出于可移植性等原因,我不喜欢使用 conio.h。此外,它不需要特别要使用cin
,因为它很有可能无法使用它,所以任何不需要我自己使用“不太好的”库进行输入的好的解决方案,将不胜感激。
【问题讨论】:
也许您需要某种异步 I/O?喜欢 epoll 或 kqueue 或 libevent2? 您希望在用户按下第一个键时,还是在输入完成时停止循环? 基本上,我根本不希望输入影响程序,因此循环是连续的,但是系统每隔一段时间就会以输入的形式获取新数据来处理 Non-blocking console input C++ 的可能重复项 【参考方案1】:为此研究多线程可能是值得的。我通常不愿建议它,因为多线程会引入许多最终难以调试的潜在问题,但在这种情况下,它们可以很容易地隔离。我设想这样的事情:
#include <atomic>
#include <chrono>
#include <iostream>
#include <thread>
int main()
std::atomic<bool> interrupted;
int x;
int i = 0;
do
interrupted.store(false);
// create a new thread that does stuff in the background
std::thread th([&]()
while(!interrupted)
// do stuff. Just as an example:
std::cout << i << std::flush;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
);
std::cin >> x;
// when input is complete, interrupt thread and wait for it to finish
interrupted.store(true);
th.join();
// apply x, then loop to make new thread to do stuff
i = x;
while(x != -1); // or some other exit condition
乍一看,这看起来有点浪费,因为它不断产生和丢弃线程,但用户输入在计算方面需要一个永恒的时间,因此开销不应过高。更重要的是,它确实具有避免任何数据竞争的优势,因为主(输入)循环和后台线程之间的唯一通信方式是原子中断标志,并且x
应用于共享数据发生当没有线程正在运行可以与主循环竞争时。
【讨论】:
虽然这个解决方案看起来很有希望,但根据其他一些地方,gcc 不支持 std::thread。除非我的编译器设置错误(如果有人可以提供帮助,那就是 Code::Blocks) gcc 已经支持std::thread
有一段时间了;我在这里用 Linux 下的 gcc 4.9 对其进行了测试。但是如果你需要支持非常老的 gcc,总是有Boost.Thread. 标准库线程主要基于它,所以它的工作方式几乎相同。编辑:也许您必须配置代码块以通过 g++ -std=c++11
选项?
@Ben 你使用了 -std=c++11 编译选项吗?
@Christophe 是的,事实上我在编译器设置中尝试了所有 3 个选项
@Wintermute 我从 OP 那里了解到,即使用户没有输入某些输入,他也想做一些事情。哇,为什么要在每次迭代中重新创建 theread,而不是在开始循环之前启动它?【参考方案2】:
免责声明:以下内容似乎适用于 Linux 上的 gcc,但由于某些原因,它不适用于 Windows 上的 VC++。规范似乎为这里的实现提供了很大的余地,VC++ 肯定会利用它......
在任何std::basic_istream
或其底层std::basic_streambuf
上都有多个可用的功能。
为了知道是否有任何字符可供输入,您可以在std::basic_streambuf
上致电in_avail
:
if (std::cin.rdbuf() and std::cin.rdbuf()->in_avail() >= 0)
in_avail
为您提供没有阻塞的可用字符数,如果没有这样的字符,它将返回-1
。之后,您可以使用常规的“格式化”读取操作,例如std::cin >> input
。
否则,对于未格式化的读取,您可以使用std::basic_istream
中的readsome
,它会返回最多 N 个可用字符而不会阻塞:
size_t const BufferSize = 512;
char buffer[BufferSize];
if (std::cin.readsome(buffer, BufferSize) >= 1)
但是要注意,这个方法的实现是高度可变的,所以对于一个可移植的程序来说它可能没有那么有用。
注意: 如评论中所述,in_avail
方法可能参差不齐。我confirm it can work,但是您首先必须使用 C++ IO 流的一个不起眼的功能:std::ios_base::sync_with_stdio(false)
,它允许 C++ 流缓冲输入(从而从 C 的 stdio 缓冲区中窃取它)。
【讨论】:
这不能移植。使用 gcc 的 libstdc++,std::cin.rdbuf()->in_avail()
总是返回 0(除非你在自己的流缓冲区中交换)。
没有一个解决方案适用于 Visual Studio:in_avail()
始终为真,readsome()
始终返回 0。
@Wintermute:我想说“有趣”,但在这一点上它甚至都不好笑,但我应该考虑一下。对于in_avail
,问题是通过使用sync_with_stdio
解决的,我将其添加为答案。
@Christophe:我什至不确定我想知道为什么,你可以试试 VC++ 上的sync_with_stdio
技巧(我没有),它可能会解决问题...
@MatthieuM。 Désollé,但它仍然不起作用!如果没有可用的字符,它总是返回 0。如果我将测试更改为 >0
而不是 >=0
,即使我输入了一些内容,它仍然是 0。似乎只要用户没有请求输入,可用字符的数量就不会更新。我不是参差不齐,但我观察到它不是便携式解决方案。【参考方案3】:
很遗憾,没有简单的便携式方法来异步检查是否按下了键。但我猜标准委员会已经仔细评估了利弊。
如果您不想依赖第三方事件管理库,并且如果多线程过于繁琐,另一种选择可能是拥有自己的 kbhit()
版本,并针对您想要支持的环境进行条件编译:
这不是最学术的答案,但它是务实的。
【讨论】:
【参考方案4】:或许,你可以试试:
while (true)
if (userInput == true)
if(cin >> input)
else
std::cout << "Invalid input!" << std::endl;
cin.clear();
//DO SOMETHING
【讨论】:
以上是关于无需等待输入即可输入?的主要内容,如果未能解决你的问题,请参考以下文章