简体   繁体   English

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

[英]C++ Interrupt or Cancel getch();

I have a simple timer. 我有一个简单的计时器。 It's in a function running in a thread separate from the main. 它位于一个与main分开的线程中运行的函数中。 Using std::future , the function returns a simple bool that says whether the timer has hit a specific number or not. 使用std::future ,该函数返回一个简单的bool,表示计时器是否已达到特定数字。

I am using getch(); 我正在使用getch(); to see if the user pressed a letter key. 查看用户是否按下了一个字母键。

If the timer returns true, that it hit a designated number, I need to cancel getch() ; 如果计时器返回true,表示它达到指定的数字,我需要取消getch() ; and move to the next step in the code. 并转到代码中的下一步。 Moving to the next step is easy. 进入下一步很容易。

It's been 2 weeks and I can not find a solution to my problem. 已经2周了,我无法找到问题的解决方案。

The Problem: How on earth, can I interrupt or cancel a call to getch(); 问题:到底如何,我可以中断或取消对getch();的调用getch(); ? Is this even possible? 这甚至可能吗?

I'm using getch(); 我正在使用getch(); to identify which letter keys were pressed. 识别按下了哪些字母键。

C++11 Visual Studio. C ++ 11 Visual Studio。

This code will allow you to do what you want, but it does not take advantage of newer language features, nor is it portable. 此代码将允许您执行您想要的操作,但它不利用较新的语言功能,也不可移植。

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

You are not going to be able to break out of getch() . 你无法摆脱getch() The best bet is to check for data in the STDIN buffer and only make the call after you have something to read. 最好的办法是检查STDIN缓冲区中的数据,并在有东西要读之后才进行调用。 This example uses kbhit() , but instead of using a polling loop where it periodically checks for buffer activity, it hooks the underlying handle to the input stream and waits for activity. 此示例使用kbhit() ,但它不是使用轮询循环来定期检查缓冲区活动,而是将底层句柄挂接到输入流并等待活动。

Using a second thread as a one-shot timer is also not the most efficient way to go. 使用第二个线程作为一次性计时器也不是最有效的方法。 The timer in this code uses a Microsoft specific object. 此代码中的计时器使用Microsoft特定对象。 It is coded to fire off every ten seconds, but you can certainly change that. 它被编码为每十秒发射一次,但你当然可以改变它。

The operating system must provide access to the keyboard. 操作系统必须提供对键盘的访问。 So on Windows for example, the best is probably to deal with input on the operating system's terms as described here . 因此,在Windows上,例如,最好的可能是描述处理上的操作系统的条款输入这里

With the standard c++ library functions one can read characters from the std::cin stream. 使用标准c ++库函数,可以从std::cin流中读取字符。 The problem is that those characters are only passed from the operating system after the user presses Enter (which also adds a newline \\n character). 问题是这些字符只有在用户按Enter键后才会从操作系统传递(这也会添加换行符\\n字符)。

If you can tolerate the need to press the return key after a character is typed then the following could work. 如果您可以在输入字符后忍受按返回键的需要,则可以使用以下内容。 This program executes get() in a separate thread so that it doesn't block the program if no key is pressed or if Enter is not pressed and only uses standard c++11. 该程序在一个单独的线程中执行get() ,这样如果没有按下任何键或者没有按下Enter键并且只使用标准的c ++ 11,它就不会阻塞程序。 This program will however not complete (ie join the thread) unless the user types q or sends the EOF . 但是,除非用户输入q发送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;
}

Output: 输出:

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

As others have mentioned, getch() is platform specific. 正如其他人所提到的,getch()是特定于平台的。 This would be a short example to do what you want to do. 这只是做你想做的事情的一个简短例子。 The basic idea is to run a non-blocking getch() in an event loop in a separate thread, and exit the event loop via a bool flag when the time limit is up. 基本思想是在单独的线程中的事件循环中运行非阻塞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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM