是否可以为std::cin设置超时

Is it possible to set timeout for std::cin?

本文关键字:cin 超时 设置 std 是否      更新时间:2023-10-16

是否可以为std::cin设置超时?例如,std::cin在10秒内没有接收到任何数据——它抛出异常或返回错误。

编辑:

Boost library的计时器呢?据我所知,这是一个可移植的图书馆。是否可以要求Boost库的计时器在预定义的时间段后抛出异常?我想它可以解决这个问题。

不可能以可移植的方式为std::cin设置超时。即使在使用不可移植的技术时,这样做也并非易事:您需要替换std::cin的流缓冲区。

在UNIX系统上,我会将std::cin使用的默认流缓冲区替换为使用文件描述符0读取输入的自定义流缓冲区。为了实际读取输入,我将使用poll()来检测输入的存在,并设置该函数的超时。根据poll()的结果,我要么读取可用的输入,要么失败。然而,为了可能处理未转发到文件描述符的类型化字符,在输入换行符之前关闭缓冲可能是合理的。

当使用多个线程时,您可以创建一个可移植的过滤流缓冲区,该缓冲区使用一个线程读取实际数据,另一个线程使用定时条件变量等待第一个线程发出接收数据的信号或等待超时到期。请注意,您需要防止虚假的唤醒,以确保在没有输入的情况下确实达到了超时。这将避免不得不修改从std::cin读取数据的实际方式,尽管它仍然替换了std::cin使用的流缓冲区,以使功能可以通过该名称访问。

我刚刚找到了如何做到这一点,轮询std::cin文件描述符。

如果发生超时且未发生任何事件,则poll函数返回0;如果发生某些事情,则返回1;如果发生错误,则返回-1。

#include <iostream>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>

bool stop = false;
void intHandler(int dummy)
{
    stop = true;
}
std::string readStdIn()
{
    struct pollfd pfd = { STDIN_FILENO, POLLIN, 0 };
    std::string line;
    int ret = 0;
    while(ret == 0)
    {
        ret = poll(&pfd, 1, 1000);  // timeout of 1000ms
        if(ret == 1) // there is something to read
        {
            std::getline(std::cin, line);
        }
        else if(ret == -1)
        {
            std::cout << "Error: " << strerror(errno) << std::endl;
        }
    }
    return line;
}
int main(int argc, char * argv[])
{
    signal(SIGINT, intHandler);
    signal(SIGKILL, intHandler);
    while(!stop)
    {
        std::string line = readStdIn();
        std::cout << "Read: " << line << std::endl;
    }
    std::cout << "gracefully shutdown" << std::endl;
}

这里发布了一个很好的答案,但作者删除了它。这是一个在我开发的应用程序中运行良好的解决方案。这就是此人所写内容的精髓:

// compile: g++ -pthread thisfile.cpp
#include <iostream>
#include <thread>
int main() {
    int x;
    bool inputReceived = false;
    time_t startTime = time(NULL);
    time_t waitTime = 10;
    std::cout << "Enter a number within " << waitTime << " secondsn";
    // spawn a concurrent thread that waits for input from std::cin
    std::thread t1([&]() {
        std::cin >> x;
        inputReceived = true;
    });
    t1.detach();
    // check the inputReceived flag once every 50ms for 10 seconds
    while (time(NULL) < startTime + waitTime && !inputReceived) {
        std::this_thread::sleep_for(std::chrono::milliseconds(50));
    }
    
    if (inputReceived) {
        std::cout << "x = " << x << "n";
        return EXIT_SUCCESS;
    }
    std::cout << "timeoutn";
    // TODO: find a way to kill the thread
    return EXIT_FAILURE;
}

请注意,线程在超时后继续运行,但当整个程序终止时,它将终止。如果这就是你所需要的,那么你就不必担心了。

然而,没有简单的方法可以杀死分离的线程。一个解决方案是关闭输入流,但这对于std::cin来说并不容易或可取。如果你幸运的话,那么你将把它与一个容易关闭的流一起使用,而不是std::cin。关闭流将导致输入语句失败,线程可能只是退出并出现内部异常,但至少线程会终止。