C ++中断或取消getch();

时间:2016-12-28 19:48:39

标签: c++ c++11 interrupt getch

我有一个简单的计时器。它位于一个与main分开的线程中运行的函数中。使用std::future,该函数返回一个简单的bool,表示计时器是否已达到特定数字。

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

如果计时器返回true,表示它达到了指定的数字,我需要取消getch();并转到代码中的下一步。转到下一步很容易。

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

问题:如何实际打断或取消对getch();的通话?这甚至可能吗?

我正在使用getch();来识别按下了哪些字母键。

C ++ 11 Visual Studio。

3 个答案:

答案 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,它就不会阻塞程序。但是,除非用户键入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

答案 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;
}