2016-12-28 201 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; 
}