2016-12-28 142 views
1

간단한 타이머가 있습니다. 그것은 메인에서 분리 된 스레드에서 실행되는 함수에 있습니다. std::future을 사용하면이 함수는 타이머가 특정 숫자에 도달했는지 여부를 나타내는 간단한 bool을 반환합니다.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 특정 개체를 사용합니다. 10 초마다 발사되도록 코드가되어 있지만 확실히 변경할 수 있습니다.

+0

코드에서 무엇이 언급되고 있는지 전반적으로 이해할 수 있지만 빛에 대한 설명이있는 각 단계에 대해 의견을 남기므로 진실을 이해할 수 있습니까? 나는 정말로 감사 할 것이다! – user7327796

+0

@ user7327796 코드에 몇 가지 설명을 추가했습니다 ... 코드를 충분히 지워 줍니까? –

+0

네, 고마워요! – user7327796

0

운영 체제는 키보드에 대한 액세스를 제공해야합니다. 예를 들어 Windows의 경우 가장 좋은 방법은 here과 같이 운영 체제의 조건에 대한 입력을 처리하는 것입니다.

표준 C++ 라이브러리 기능을 사용하면 std::cin 스트림의 문자를 읽을 수 있습니다. 문제는 해당 문자가 사용자가 을 누른 후에 만 ​​운영 체제에서 전달된다는 것입니다.을 입력하십시오 (줄 바꿈 문자 \n도 추가됨).

문자를 입력 한 후에 리턴 키를 눌러야하는 경우 다음을 수행하면됩니다. 이 프로그램은 별도의 스레드에서 get()을 실행하여 아무 키도 누르지 않거나 을 입력하지 않고 표준 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 
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; 
}