输入C 中的超时

Input with a timeout in C++

本文关键字:超时 输入      更新时间:2023-10-16

我想拥有一个程序,其中用户有10秒钟输入密码。如果计时器超过10秒,则该程序将显示一条消息。我当前的代码是:

 #include <iostream>
 #include <ctime>
 #include <string>
int main(){
std::string password;
int start_s=clock();
int stop_s=clock();
if(stop_s-start_s <= 0){
    std::cout << "TIME RAN OUT!";
}
std::cout << "Enter your password! n";
std::cout << "Password: ";
std::cin >> password;
std::cout << "n n";
if (password == "password123"){
    std::cout << "Correct!";
} else {
    std::cout << "Wrong!";
}
}

这当然不起作用...但是我不确定下一步该怎么做...

如果您需要更多详细信息,请在评论中询问。

编辑:

我刚刚意识到问题是什么...它花了时间戳记,然后迅速制作了另一个时间戳记。当发现差异时,它低于0 ...

但我仍然不知道下一步该怎么做...

您要做的是要以10秒的超时范围从stdin读取非阻滞(异步)。这并不是太难了,但可能会根据您当前的水平涉及许多新概念。

这里的关键概念是cin >> password;是A 阻止调用,即直到完成为止,该代码中的控制不会进一步流动。因此,我们需要以某种方式使其不受阻碍,或者在超时到期时将其阻止并脱离。

根据系统的设计要求和约束,有一些常见的实现。每个实现都不同于OS,但技术非常相似。

1。异步:与超时的stdin 这种方法通常用于网络编程中,可以扩展到其他形式的输入,例如当前情况。

  1. 将标准输入(stdin)句柄(句柄= 0)放入"手表列表"。
  2. 将暂停放在手表列表上。
  3. 每当stdin发生变化时,请处理。
  4. 超时到期后,检查我们处理的工作是否完成工作。

在Linux(以及许多其他Unix口味)中,可以使用FD_SETselect系统调用来处理手表列表。在Windows中,您需要使用WaitForMultipleEvents

我不确定我是否可以为此目的准确解释这些概念。作为参考,这里有一些代码指针的另一个问题在这里。

2。同步:多线程与中断这是一种常用技术,用于我们需要细粒度螺纹式螺旋/计时器的情况。

  1. 创建两个线程AB
  2. A将在指示的超时等待。
  3. B将等待阻止读取
  4. 如果AB完成之前终止(Times Out),则A信号BB决定下一步该怎么做(终止,重复消息等)
  5. 如果B读取密码并且很好,B信号A并要求它死。

实现同样的方法的另一种方法是使OS中断线程B如其中的一个注释。

3。同步:投票这是用于我们不需要过多的细粒度控制的情况。

  1. 检查输入中是否使用非阻滞读取(kbhit()
  2. 是否有任何内容
  3. 如果没有,如果超时存在时间,请等待较小的时间delta(例如10ms
  4. 如果超时已经过期并且没有更多的时间,请执行任何需要处理(消息用户,退出等)

请注意,在这种情况下,根据delta,该方法可能会消耗大量CPU,并且可能效率低下。例如,如果delta=10ms如上所述,则该线程将每秒唤醒100次,并且不会有效,尤其是当用户在其键盘上未在键盘上输入字符时。

此代码使用线程进行。

#include <iostream>
#include <thread>
#include <chrono>
#include <string>
int main()
{
   std::string password;
   std::cout << "Enter your password! n";
   std::cout << "Password: ";
   //try to read a passsword in 10 seconds
   std::thread t1([&]() {
       std::cin >> password;
       });
   std::this_thread::sleep_for(std::chrono::seconds(10));
   t1.detach();
   //check if password was entered
   if (password.empty())
       std::cout << "TIME IS OUTn";
   else
       if (password == "password123")
           std::cout << "Correct";
       else
           std::cout << "Worng";
   return 0;
}

但是,如果您查看,即使用户在时间上输入了Passsword,也可以看到它等待10秒的末尾。因此您可以这样改进:

#include <iostream>
#include <thread>
#include <chrono>
#include <string>
int main()
{
    std::string password;
    std::cout << "Enter your password! n";
    std::cout << "Password: ";

    time_t start = time(NULL);
    time_t waitTime = 10;
    //try to read a passsword in 10 seconds
    while (time(NULL) < start + waitTime && password.empty())
    {
        std::thread t1([&]() {
            std::cin >> password;
            });
        std::this_thread::sleep_for(std::chrono::milliseconds(20));
        t1.detach();
    }
    
    
    //check if password was entered
    if (password.empty())
        std::cout << "nTIME IS OUTn";
    else
        if (password == "password123")
            std::cout << "Correct";
        else
            std::cout << "Worng";
    return 0;
}

我最近遇到了同一问题。我从 @user1952500中找到答案,呈现有趣的方法。其中,第二个是我更容易理解的。

但是,我还没有为此找到好的代码解决方案。更一般而言,使用分离不会杀死线程,但它让独立运行("主线程之外")。这意味着线程将永远运行,直到整个代码终止为止,如果我们分离多个线程,这甚至是一个更糟糕的情况。

要使用螺纹在超时使用超时完成键盘输入,必须从另一个线程中杀死该线程。问题是:不可能仅使用C 标准库进行操作。

有必要使用依赖平台的功能。这是我对Linux的解决方案:

#include <thread>
#include <chrono>
#include <stdio.h>
#include <pthread.h>
#include <string>
std::chrono::milliseconds now_ms(){
    return std::chrono::duration_cast<std::chrono::milliseconds>(
        std::chrono::system_clock::now().time_since_epoch()
    );
}

int main(){
    // set timeout
    std::chrono::milliseconds timeout(10000);
    // variable to check if the thread terminated by itself
    bool thread_terminated = false;
    // variable to store input 
    std::string answer;
    // create thread
    std::thread t1([&]() {
        std::cin >> answer;
        thread_terminated = true;
    });
    // get native handle
    auto t_handle = t1.native_handle();
    // start the thread
    auto start_ts = now_ms();
    t1.detach()
    // check for timeout or answer received
    while(this->now_ms_d() - start_ts < timeout && answer.empty()){
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
    // if the thread didn't terminate, kill it
    if(! thread_terminated) pthread_cancel(t_handle);
    
    if (answer.empty()){
        std::cout << "no answer received" << std::endl;
    }
    else{
        /* do what you need to do with answer */
    }
    return 0;
}

对于不同的平台,您需要调用其他功能,而不是pthread_cancel(t_handle)

例如,在Windows上,您可以使用terminateThread。

典型的C 解决方案应该看起来像:

#include <iostream>
#include <chrono>
#include <string>
int main() {
     std::string password;
     std::cout << "Enter your password! n";
     std::cout << "Password: ";
     auto start_s = std::chrono::system_clock::now();
     std::cin >> password;
     auto stop_s = std::chrono::system_clock::now();
     if(stop_s - start_s >= std::chrono::seconds(10)) {
        std::cout << "TIME RAN OUT!";
     }
     else if (password == "password123") {
            std::cout << "Correct!";
     } else {
         std::cout << "Wrong!";
     }
}