【问题标题】:Non-Blocking i/o in c? (windows)c语言中的非阻塞i/o? (视窗)
【发布时间】:2014-02-13 04:57:24
【问题描述】:

我正在尝试在 Windows 终端应用程序上获得非阻塞 I/O(仅限 Windows,抱歉!)。

如果我想有一个短的输入时间,用户可以按下一个按钮,但如果他没有停止输入并且程序继续?

例如:

一个从 1 计数到用户按下某个键时停止的计时器: 我应该有一个 while 循环,但如果我执行 getch 或 getchar 函数,它会停止程序,对吗?

我知道我可以使用 kbhit(); ,但对于“程序”,我试图让我需要知道输入,而不仅仅是如果有输入! 是否有任何简单的功能可以让我像键盘缓冲区中的最后一个键一样读取?

【问题讨论】:

  • 是的,_kbhit() 告诉你有输入。调用_getch()实际读取,不会阻塞。

标签: c io nonblocking


【解决方案1】:

来自documentation for _kbhit()

_kbhit 函数检查控制台是否有最近的击键。如果函数返回非零值,则表示缓冲区中正在等待击键。然后程序可以调用_getch_getche 来获取按键。

所以,在你的循环中:

while (true) {
    // ...
    if (_kbhit()) {
        char c = _getch();
        // act on character c in whatever way you want
    }
}

所以,您仍然可以使用_getch(),但仅限在_kbhit() 表示有东西在等待之后才使用。这样就不会阻塞了。

【讨论】:

  • 我让这个问题更“可读”了,抱歉,我现在就试试这个。
  • 谢谢你!这不是我正在寻找的确切解决方案,在带有 ncurses 的 linux 上可能会有更好的解决方案,但它确实有效!
  • 是的,好吧,您确实专门询问了 Windows,而不是 Linux。 :)
  • 是的,我的意思是我可以用 ncurses 以更好的方式做到这一点,但由于我专门询问 Windows,你的解决方案是我迄今为止找到的唯一解决方案,谢谢!但还有一个问题:_kbhit() 和 kbhit() 或 getch() 和 _getch() 有什么区别?
  • 微软将kbhit等所有POSIX函数重命名为_kbhit。功能是等效的。
【解决方案2】:

以下是如何使用正确的 API 在 Windows 中对标准输入进行非阻塞调用:

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>

void ErrorExit(LPSTR);
void KeyEventProc(KEY_EVENT_RECORD ker);

// Global variables are here for example, avoid that.
DWORD fdwSaveOldMode;
HANDLE hStdin;

void printToCoordinates(int x, int y, char* text)
{
    printf("\033[%d;%dH%s", y, x, text);
}

int main()
{
    printf("\033[H\033[J");
    int i = 0;
    char* s = "*";

    DWORD fdwMode, cNumRead;
    INPUT_RECORD irInBuf[128];
    DWORD bufferSize = 0;

    hStdin = GetStdHandle(STD_INPUT_HANDLE);

    // Just a check to ensure everything is fine at this state
    if (hStdin==INVALID_HANDLE_VALUE){
        printf("Invalid handle value.\n");
        exit(EXIT_FAILURE);
    }

    // Just a check to ensure everything is fine at this state
    if (! GetConsoleMode(hStdin, &fdwSaveOldMode) )
        ErrorExit("GetConsoleMode");

    // Those constants are documented on Microsoft doc
    // ENABLE_PROCESSED_INPUT allows you to use CTRL+C
    // (so it's not catched by ReadConsoleInput here)
    fdwMode = ENABLE_WINDOW_INPUT | ENABLE_PROCESSED_INPUT;
    if (! SetConsoleMode(hStdin, fdwMode) )
        ErrorExit("SetConsoleMode");


    while (i < 60) {
        // The goal of this program is to print a line of stars
        printToCoordinates(i, 5, s);
        i++;


        GetNumberOfConsoleInputEvents(hStdin, &bufferSize);

        // ReadConsoleInput block if the buffer is empty
        if (bufferSize > 0) {
            if (! ReadConsoleInput(
                    hStdin,      // input buffer handle
                    irInBuf,     // buffer to read into
                    128,         // size of read buffer
                    &cNumRead) ) // number of records read
                ErrorExit("ReadConsoleInput");

            // This code is not rock solid, you should iterate over
            // irInBuf to get what you want, the last event may not contain what you expect
            // Once again you'll find an event constant list on Microsoft documentation
            if (irInBuf[cNumRead-1].EventType == KEY_EVENT) {
                KeyEventProc(irInBuf[cNumRead-1].Event.KeyEvent);
                Sleep(2000);
            }
        }

        Sleep(100);
    }
    // Setting the console back to normal
    SetConsoleMode(hStdin, fdwSaveOldMode);
    CloseHandle(hStdin);

    printf("\nFIN\n");

    return 0;
}

void ErrorExit (LPSTR lpszMessage)
{
    fprintf(stderr, "%s\n", lpszMessage);

    // Restore input mode on exit.

    SetConsoleMode(hStdin, fdwSaveOldMode);

    ExitProcess(0);
}

void KeyEventProc(KEY_EVENT_RECORD ker)
{
    printf("Key event: \"%c\" ", ker.uChar.AsciiChar);

    if(ker.bKeyDown)
        printf("key pressed\n");
    else printf("key released\n");
}

请注意这项工作在全新的终端应用程序中,而不是在 CMD 中(由于代码中使用了 termcaps),但它会编译并且您仍然可以运行它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-24
    • 1970-01-01
    • 1970-01-01
    • 2018-04-06
    • 2016-09-16
    • 2012-06-18
    • 1970-01-01
    相关资源
    最近更新 更多