我有一个简单的计时器。它位于一个与main分开的线程中运行的函数中。使用std::future
,该函数返回一个简单的bool,表示计时器是否已达到特定数字。
我正在使用getch();
查看用户是否按了一个字母键。
如果计时器返回true,表示它达到了指定的数字,我需要取消getch()
;并转到代码中的下一步。转到下一步很容易。
已经有2周了,我无法找到解决问题的方法。
问题:如何实际打断或取消对getch();
的通话?这甚至可能吗?
我正在使用getch();
来识别按下了哪些字母键。
C ++ 11 Visual Studio。
答案 0 :(得分:0)
此代码将允许您执行您想要的操作,但它不会利用较新的语言功能,也不会移植。
events[0] = CreateEvent(NULL,FALSE,FALSE,NULL); // Obtain a Windows handle to use with a timer
events[1] = GetStdHandle(STD_INPUT_HANDLE); // Get a Windows handle to the keyboard input
// Create a timer object that will strobe an event every ten seconds
DemoTimer = timeSetEvent(10000,0,(LPTIMECALLBACK)events[0],NULL,TIME_PERIODIC|TIME_CALLBACK_EVENT_SET);
while (done == false)
{
// Wait for either the timer to expire or a key press event
dwResult = WaitForMultipleObjects(2,events,false,INFINITE);
if (dwResult == WAIT_FAILED)
{
dwResult = GetLastError();
done = true;
}
else
{
if (dwResult == WAIT_OBJECT_0) // WAIT_OBJECT_0 corresponds to the timer event
{
DoTimeoutEvent();
}
else
{
// Any other event will be a keypress
if (_kbhit() != 0) // Verify that a key was pressed so that we do not block when we query for a value
{
int val = _getch();
// At this point, we process the key value
}
}
}
}
你无法突破getch()
。最好的办法是检查STDIN缓冲区中的数据,并在有东西要读之后才进行调用。此示例使用kbhit()
,但不是使用轮询循环来定期检查缓冲区活动,而是将底层句柄挂接到输入流并等待活动。
使用第二个线程作为一次性计时器也不是最有效的方法。此代码中的计时器使用Microsoft特定对象。它被编码为每十秒发射一次,但你当然可以改变它。
答案 1 :(得分:0)
操作系统必须提供对键盘的访问。因此,在Windows上,最好的方法是处理操作系统术语的输入,如here所述。
使用标准c ++库函数,可以从std::cin
流中读取字符。问题是这些字符只有在用户按 Enter (它还添加换行符\n
字符)后才会从操作系统传递。
如果您在键入字符后可以忍受按返回键的需要,则以下操作可能有效。该程序在一个单独的线程中执行get()
,这样如果没有按下任何键或者没有按下 Enter 并且只使用标准的c ++ 11,它就不会阻塞程序。但是,除非用户键入q
或sends the EOF
,否则此程序将无法完成(即加入主题)。
#include <iostream>
#include <string>
#include <chrono>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
std::condition_variable cv{};
std::mutex mtx;
std::queue<char> char_queue{};
bool quit{false};
void add_chars_to_queue()
{
char c{};
for(;;) {
c = static_cast<char>(std::cin.get());
if(!std::cin) {
std::unique_lock<std::mutex> lck{mtx};
quit = true;
cv.notify_all();
return;
}
if(c == 'q' || c == 'Q') {
std::unique_lock<std::mutex> lck{mtx};
quit = true;
char_queue.push(c);
cv.notify_all();
return;
}
if(c == '\n')
continue;
std::unique_lock<std::mutex> lck{mtx};
char_queue.push(c);
cv.notify_all();
}
}
std::string get_key_or_wait(std::chrono::system_clock::duration d)
{
std::unique_lock<std::mutex> lck{mtx};
for(int i{10}; i > 0; --i) {
cv.wait_for(lck, d / 10., []() {return quit || !char_queue.empty(); });
if(!char_queue.empty())
break;
if(quit)
return{"Quitting.\n"};
std::cout << "Countdown at " << i << '\n';
}
std::string return_string{};
if(!char_queue.empty()) {
return_string += "Obtained a character from the stream before the timer ran out. Character was: ";
return_string += char_queue.front();
char_queue.pop();
}
else {
return_string = "Timer ran out.";
}
return return_string;
}
int main()
{
std::thread get_chars{[]() {add_chars_to_queue(); }};
std::cout << "Type q to exit.\n";
for(int i{}; i < 3; ++i) {
{
std::lock_guard<std::mutex> lck{mtx};
if(quit)
break;
}
std::cout << "Waiting for key press followed by <enter>.\n";
std::cout << get_key_or_wait(std::chrono::seconds(10)) << '\n';
}
get_chars.join();
return 0;
}
输出:
Type q to exit.
Waiting for key press followed by <enter>.
Countdown at 10
Countdown at 9
Countdown at 8
a
Obtained a character from the stream before the timer ran out. Character was: a
Waiting for key press followed by <enter>.
Countdown at 10
Countdown at 9
Countdown at 8
Countdown at 7
Countdown at 6
Countdown at 5
Countdown at 4
Countdown at 3
Countdown at 2
Countdown at 1
Timer ran out.
Waiting for key press followed by <enter>.
Countdown at 10
Countdown at 9
Countdown at 8
bCountdown at 7
Countdown at 6
Countdown at 5
Obtained a character from the stream before the timer ran out. Character was: b
q
答案 2 :(得分:0)
正如其他人所提到的,getch()是特定于平台的。这只是做你想做的事情的一个简短例子。基本思想是在单独的线程中的事件循环中运行非阻塞getch(),并在时间限制结束时通过bool标志退出事件循环。
#include <iostream>
#include <thread>
#include <chrono>
#include <future>
#include <conio.h>
#include <Windows.h>
int nonBlockingGetChar();
int nonBlockingGetCharTask();
//This should be atomic. but I'm skipping it right here'
static bool getCharAlive{ false };
int main()
{
//Timeout
static const long long TIMEOUT{ 1000 * 5 };
auto startTime = std::chrono::high_resolution_clock::now();
auto endTime = std::chrono::high_resolution_clock::now();
long long elapsedMilliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count();
std::future<int> getCharHandle{ std::async(std::launch::async, nonBlockingGetCharTask) };
do {
//Other code here
endTime = std::chrono::high_resolution_clock::now();
elapsedMilliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count();
if (elapsedMilliseconds >= TIMEOUT) {
//If the timer hit a certain amount, cancel the getChar task
getCharAlive = false;
while (getCharHandle.wait_for(std::chrono::seconds(0)) != std::future_status::ready) {
//Wait for getCharAlive to exit
}
std::cout << "User did not enter anything in the alotted time" << std::endl;
break; //Move on to next step
} else {
//Otherwise, check if the getCharTask returned anything
if (getCharHandle.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
int userInput{ getCharHandle.get() };
if (userInput == -1) {
std::cout << "User did not enter anything in the alotted time" << std::endl;
} else {
std::cout << "User entered keycode " << userInput << std::endl;
//Do whatever with the user input
}
break; //Move on to next step
}
}
} while (true);
//And so on to step 2
}
int nonBlockingGetChar()
{
if (_kbhit()) {
return _getch();
} else {
return -1;
}
}
int nonBlockingGetCharTask()
{
getCharAlive = true;
do {
int returnValue{ nonBlockingGetChar() };
if (returnValue != -1) {
return returnValue;
}
} while (getCharAlive);
return -1;
}