2016-12-28 199 views
1

我有一个简单的计时器。它在一个独立于主体的线程中运行。使用std::future,函数返回一个简单的布尔值,表示计时器是否已经击中了特定的数字。C++中断或取消getch();

我正在使用getch();来查看用户是否按下了一个字母键。

如果定时器返回true,那它碰到一个指定的号码,我需要取消getch();并转到代码中的下一步。转移到下一步很容易。

已经2周了,我找不到解决我的问题的方法。

问题:怎样才能中断或取消对getch();的呼叫?这甚至有可能吗?

我使用getch();来确定哪些字母键被按下。

C++ 11 Visual Studio。

+5

不,这是一个来自黑暗时代的陈旧功能,不应再使用!你应该在'STDIN'上做'epoll'。 –

+0

我正在使用getch();检索密钥的ascii值。那些做同样的事情吗? – user7327796

+1

是的,有很多方法可以轮询_standard input_(或任何其他文件描述符)以进行活动并处理其中收到的数据。 'getch()'就是其中之一,但它有极大的局限性,正如你发现的那样。这甚至不是标准的;它是一个DOS函数,并且(我认为)ncurses再现了它。 –

回答

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特定的对象。它被编码为每十秒钟发射一次,但你当然可以改变它。

+0

我可以对代码中所说的内容有一个大概的了解,但是能否用一些简单的解释来评论每个步骤,以便我能够真正理解发生了什么?我会非常感激! – user7327796

+0

@ user7327796我在代码中添加了一些注释...他们是否充分清除它? –

+0

是的,谢谢! – user7327796

0

操作系统必须提供对键盘的访问。所以在Windows上,最好的办法可能是按照操作系统的条款处理输入here

使用标准的C++库函数,可以读取std::cin流中的字符。问题是这些字符只能在用户按输入(它也会添加一个换行符\n字符)后才从操作系统传递。

如果您可以容忍在键入字符后按下返回键的需要,那么以下内容可以工作。该程序在一个单独的线程中执行get(),这样,如果没有按键被按下或者输入未被按下并且仅使用标准C++ 11,它不会阻止程序。但是,除非用户输入qsends 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 
0

正如其他人所说,残培()是特定的平台。这将是一个简单的例子来做你想做的事情。基本思想是在一个单独的线程的事件循环中运行一个非阻塞的getch(),并在时间限制结束时通过布尔标志退出事件循环。

#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; 
}