使用std::cin语句实现自动超时

3
我编写了这个程序。
#include<iostream>
using namespace std;
int n;
int main(int argc, char *argv[])
{
  std::cout << "Before reading from cin" << std::endl;

   // Below reading from cin should be executed within stipulated time
   bool b=std::cin >> n;
   if (b)
      std::cout << "input is integer for n and it's correct" << std::endl;
   else
      std::cout << "Either n is not integer or no input for n" << std::endl;
  return 0;
}

这里的std::cin语句会等待控制台输入,直到我们提供一些输入并按Enter键后才进入睡眠模式。

我希望在10秒后,如果用户没有输入任何数据,std::cin语句会超时,编译器会执行在std::cin语句下面的程序的下一条语句。

我可以使用多线程机制来解决这个问题。以下是我的代码:

#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>

#include<iostream>
using namespace std;
void *thread_function(void *arg);
int input_value;

int main(int argc, char *argv[])
{
    int res;
    pthread_t a_thread;
    void *thread_result;
    res=pthread_create(&a_thread,NULL,thread_function,NULL);
    if(res!=0){
            perror("Thread creation error");
            exit(EXIT_FAILURE);
    }
    //sleep(10);
    cout<<"cancelling thread"<<endl;
    res=pthread_cancel(a_thread);

    cout<<"input value="<<input_value<<endl;
    exit(EXIT_SUCCESS);
 }
 void *thread_function(void *arg)
 {
    int res;
    res=pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
    if(res!=0){
            perror("Unable to set pthread to cancel enbable state");
            exit(EXIT_FAILURE);
    }
    cin>>input_value;
    pthread_exit(&input_value);
 }

但是我面临一个问题。由于睡眠函数,无论用户是否输入值,睡眠函数默认都会休眠10秒钟。这就是我的滞后之处。

如何解决这个问题,比如使用(信号、二进制信号量等)。请将您的答案与我的解决方案相关联(即多线程)。

欢迎提供任何信息...


一个定时等待条件可能是你在这里寻找的。 - cli_hlt
3
可能是重复问题:如何使从 `std::cin` 读取的内容在特定时间后超时。你不应该一遍又一遍地问基本相同的问题。 - mvp
@mvp 自从我编辑了问题但是没有得到任何人的回答,所以我发布了一个新的问题。 - Santosh Sahu
2个回答

5

如果您使用的是POSIX机器,您可以使用例如select来检查标准输入中是否有任何内容:

fd_set fds;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds)

timeval timeout;
timeout.tv_sec = 5;   // A five-second timeout
timeout.tv_usec = 0;

int rc = select(STDIN_FILENO + 1, &fds, nullptr, nullptr, &timeout);
if (rc < 0)
    perror("select");
else if (rc == 0)
{
    // Timeout
}
else
{
    // There is input to be read on standard input
}

使用poll(如Basile Starynkevitch所建议的)可以像这样完成:
struct pollfd poller;
poller.fd = STDIN_FILENO;
poller.events = POLLIN;
poller.revents = 0;

int rc = poll(&poller, 1, 5);  // Poll one descriptor with a five second timeout
if (rc < 0)
    perror("select");
else if (rc == 0)
{
    // Timeout
}
else
{
    // There is input to be read on standard input
}

请针对我的答案解释一些机制。我是指对我的答案进行任何改进。 - Santosh Sahu
2
@SantoshSahu,使用我的解决方案,您不需要任何线程。如果您不需要任何线程,您也不需要任何同步。 - Some programmer dude
我建议使用poll(2)而不是select(2) - Basile Starynkevitch
@BasileStarynkevitch:在这个简单的例子中,selectpoll之间没有可衡量的差异。然而,select更古老且被普遍支持。 - mvp

1

我尝试了几种方法来让它工作,我的当前解决方案非常临时,但是对于其他使用选择和轮询的解决方案失败的情况下,它对我很有效。

我的代码有时会给我一个数据可用的正面结果,但然后永远阻塞在std::cin.get();std::getline(std::cin, STRINGVARIABLE);

我的解决方案:

// Reset flag, initially true so that if cin blocks it will be reset
bool Reset = true;
// Create a c++11 thread to reset cin after a timeout of 100ms
std::thread ResetThread([&Reset](void){
    // This thread will wait 100ms
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    // Then if the reset flag is still true it will reset cin by closing and opening it
    if (Reset) {
        close(STDIN_FILENO);
        // TODO: Really the output of this open call should be checked for errors
        open("/dev/null", O_RDONLY);
    }
});

// Now for the standard reading of cin:
std::string Line;
std::getline(std::cin, Line);.
// If cin was read correctly the getline command will return immediately
// This means that the reset flag will be set to false long before the thread tests it.
Reset = false;
// Finally join with the thread.
ResetThread.join();

这段代码可以与select或poll方法结合使用(它在我的代码中),以防止每100毫秒创建一个新线程。

显然,此代码的适用性取决于项目的其余部分,但对我有效,所以我想分享一下。

-- 编辑 --

在移动电脑之后,似乎这段代码并不像我希望的那样健壮。我现在只接受使用单独的阻塞线程来处理输入,请参见我的答案:

经过长时间与非阻塞cin的斗争,似乎唯一稳健的方法是将其置于自己的线程中。

请查看此处的实现方式:

https://stackoverflow.com/a/39499548/1427932


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接