我想要一个程序,让用户有 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...
但我还是不知道下一步该做什么...
您想要做的是从
stdin
进行非阻塞(异步)读取,超时时间为 10 秒。这并不太难,但根据您当前的水平可能会涉及许多新概念。
这里的关键概念是
cin >> password;
是一个阻塞调用,即,在完成之前,控制不会在此代码中进一步流动。所以我们需要以某种方式使其成为非阻塞,或者保持阻塞并在超时到期时打破它。
根据系统的设计要求和约束,有一些常见的实现方式。每个实现都因操作系统而异,但技术非常相似。
1。异步:带超时的 STDIN 这种方法通常用于网络编程,并且可以扩展到其他形式的输入,例如当前的情况。
在 Linux(以及许多其他 Unix 版本)中,可以使用
FD_SET
和 select
系统调用来处理监视列表。在 Windows 中,您需要使用 WaitForMultipleEvents
。
我不确定我是否能够出于这个问题的目的准确地解释这些概念。作为参考,另一个问题有一些完全相同的代码指针是here。
2。同步:带中断的多线程 这是一种常用技术,用于我们需要细粒度事件调度程序/计时器的情况。
A
和B
。A
将等待指定的超时时间。B
将等待阻塞读取A
在 B
完成之前终止(超时),A
会向 B
发出信号,并且 B
决定下一步要做什么(终止、重复消息等)B
读取密码并且没有问题,B
会向 A
发出信号并要求其死亡。实现相同目的的另一种方法是使操作系统中断线程
B
,如其中一个评论中所述。
3.同步:轮询 这用于我们不需要对时间进行太多细粒度控制的情况。
kbhit()
)delta
(说10ms
)请注意,在这种情况下,根据
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 OUT\n";
else
if (password == "password123")
std::cout << "Correct";
else
std::cout << "Worng";
return 0;
}
但是如果你检查一下,你会发现即使用户及时输入了密码,它也会等到 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 OUT\n";
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(){
// 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 < this->_cin_timeout && answer.empty()){
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
// 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!";
}
}