C# 在控制台应用程序中等待键盘事件

C# 在控制台应用程序中等待键盘事件,c#,async-await,C#,Async Await,有没有一种方法可以让任务在没有专用线程的情况下完成并返回按键 // Kernel callback, not a new thread in my process waiting for a keyboard event var key = await KeyPress(); AsConsole.ReadKey()是一个阻塞调用,只使用线程等待用户输入。您可以打开标准输入流,该流具有异步读取操作: using (var stream = Console.OpenStandardInput())

有没有一种方法可以让
任务
在没有专用线程的情况下完成并返回按键

// Kernel callback, not a new thread in my process waiting for a keyboard event
var key = await KeyPress();

As
Console.ReadKey()
是一个阻塞调用,只使用线程等待用户输入。

您可以打开标准输入流,该流具有异步读取操作:

using (var stream = Console.OpenStandardInput())
{
    var buffer = new byte[1];
    var bytesRead = await stream.ReadAsync(buffer, 0, 1);
    char c = (char)buffer[0];
    Console.WriteLine(c);
}
这就是问题所在,一个只用于等待用户输入的线程听起来像是浪费 (不一定是大的,但感觉应该有一个 这方面的实施)

对于需要用户输入的控制台应用程序,我不会担心这一点

无论如何,通过使用一些底层的Win32 API可能实现您所追求的目标。这些文件的内容如下:

进程可以在其中一个等待中指定控制台输入缓冲区句柄 用于确定何时存在未读控制台输入的函数。当 输入缓冲区不为空,控制台输入缓冲区句柄的状态 有信号。确定数据库中未读输入记录数的步骤 控制台的输入缓冲区,使用
GetNumberOfConsoleInputEvents
功能。从控制台输入缓冲区读取输入记录 影响未读记录的数量,请使用
peek控制台输入
功能。要丢弃控制台输入缓冲区中的所有未读记录, 使用
FlushConsoleInputBuffer
功能

因此,理论上,您可以使用返回的句柄并将其传递给 . 然后,您可以使用
TaskCompletionSource
将其转换为可等待的任务,如所述


我还没有在实践中证实这一点。它不应该阻挡一条线索,但在我看来,这场比赛还是不值得的

像这样@一个线程仍在忙于循环和运行readkey。您不同意使用一个专用(但只有一个)线程来阻止等待控制台输入吗?然后,您可以使用类似consumer/producer模式的方式在其他线程上异步使用控制台输入。@就是这样,一个只用于等待用户输入的线程听起来像是浪费(不一定很大,但感觉应该有一个实现).
ReadAsync
不会返回,除非按enter键。@user4388177这是控制台的固有问题;它缓冲输入。将ReadConsoleInput本身与锁一起使用,以防止多个线程同时调用它。必须复制正确的实现that@PanagiotisKanavos,我不太明白你的意思。OP不需要
Console.ReadKey
(或
ReadConsoleInput
)来异步等待输入。他只需要std输入句柄,并向
RegisterWaitForSingleObject
注册回调。然后,一旦收到可用输入的通知,他就可以使用
ReadKey
在当前的任何线程上读取它。只要他不同时从多线程调用
ReadKey
,我想这可能就是解决方案,我会尝试实现它并给出一些反馈。我当时正在看一些内核回调来包装成TaskCompletionSource;工作正常,但是
RegisterWaitForSingleObject
的文档说明:“指示线程池中的等待线程等待该对象。当出现以下情况之一时,等待线程将指定的回调函数排队等待到线程池”因此我想它无论如何都会阻止线程。只是以一种更奇特和复杂的方式。编辑:也许我错了,一个等待线程是不同的……这个实验是有效的,但是在键盘输入之外的很多事件中,句柄都会收到信号,实际上它会触发更多线程,从而降低性能。我会认为你的答案是正确的,因为它非常有用,而且在理论上是正确的。