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

Posted

技术标签:

【中文标题】是否可以为 std::cin 设置超时?【英文标题】:Is it possible to set timeout for std::cin? 【发布时间】:2012-02-21 14:46:40 【问题描述】:

例如,std::cin 在 10 秒内没有收到任何数据 - 它会抛出异常或返回错误。

已编辑:

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

【问题讨论】:

Setting a timeout on ifstream in C++?的可能重复 Dave,我需要开发一个可移植的程序。 你可以试试Boost.Asio 可以使用 C++11 中的包装器轻松完成(将来某个时候:-P) @larsmans:不完全是重复的,因为 OP 没有询问 linux 特定的问题。 【参考方案1】:

无法以便携方式为std::cin 设置超时。即使采用非便携式技术,这样做也不是一件容易的事:您需要替换 std::cin 的流缓冲区。

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

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

【讨论】:

那么 Boost 库中的计时器呢?据我所知,它是便携式库。是否可以要求 Boost 库的计时器在预定义的时间段后抛出异常?我想它可以解决这个问题。 当您尝试从std::cin 读取时,它将阻塞该线程并且不会返回。据我所知,没有任何可移植的东西可以中断系统调用。 好的,谢谢Dietmar Kühl先生。 我对文件描述符一无所知,所以我听不懂你的句子我会用一个自定义的流缓冲区替换 std::cin 使用的默认流缓冲区,它使用文件描述符 0 来读取输入 。文件缓冲区与文件描述符有什么关系? , 为什么我们需要这样做?另外,你能给我链接一些好文章吗? ,非常感谢:) @Mr.Anubis:文件描述符是各种流输入(文件、键盘输入、套接字等)的低级 POSIX 抽象。 POSIX 进程的标准输入使用文件描述符0,这就是stdinstd::cin 所连接的。 std::cin 的默认实现不支持设置超时,但是使用系统调用poll() 可以在指定时间段内没有任何事件时超时。【参考方案2】:

我刚刚想出了如何做到这一点,轮询 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;

【讨论】:

在 Linux 上,select()getline(std::cin, ...) 结合起来对我不起作用,但 poll() 可以。 poll() 在 Windows 中是 WASPoll,这似乎是可移植的:***.com/a/28947000/1831722【参考方案3】:

这里发布了一个很好的答案,但作者删除了它。在我正在开发的应用程序中,这是一个非常适合我的解决方案。这是该人所写内容的精髓:

// 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 << " seconds\n";

    // 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 << "timeout\n";
    // TODO: find a way to kill the thread
    return EXIT_FAILURE;

注意线程在超时发生后继续运行,但是当整个程序终止时它会终止。如果这就是您所需要的,那么您无需担心。

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

【讨论】:

以上是关于是否可以为 std::cin 设置超时?的主要内容,如果未能解决你的问题,请参考以下文章

是否可以将网络调用超时设置为 60 秒以上。

我可以为单个查询设置 JDBC 超时吗?

pycharm远程连接超时

mysql怎么设置超时时间

如何使用 ThreadPoolTask​​Executor 为任务设置超时

我可以在 C# 中为 UdpClient 设置超时吗?