为C#控制台应用实现带倒计时的输入功能遇阻
控制台带倒计时的输入解决方案
针对你的需求——每个问题显示10秒倒计时,用户输入则终止当前倒计时跳转下一题,无输入则倒计时结束后自动跳转——下面是完整的可运行实现,解决你遇到的Console.ReadLine()无法终止、倒计时不显示的问题:
完整代码
using System; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; class Program { const int STD_INPUT_HANDLE = -10; [DllImport("kernel32.dll", SetLastError = true)] internal static extern IntPtr GetStdHandle(int nStdHandle); [DllImport("kernel32.dll", SetLastError = true)] static extern bool CancelIoEx(IntPtr handle, IntPtr lpOverlapped); static async Task Main(string[] args) { for (int i = 0; i < 3; i++) { Console.WriteLine($"=== 问题 {i + 1} ==="); using var cts = new CancellationTokenSource(); var countdownTask = DisplayCountdownAsync(cts.Token); // 将同步的Console.ReadLine包装到Task中,实现并行 var inputTask = Task.Run(() => { try { return Console.ReadLine(); } catch (OperationCanceledException) { return null; } }); // 等待输入或倒计时任意一个完成 var completedTask = await Task.WhenAny(countdownTask, inputTask); // 取消未完成的任务 cts.Cancel(); if (completedTask == inputTask) { var userInput = await inputTask; Console.WriteLine($"\n你输入了: {userInput}"); // 强制终止控制台输入的阻塞状态 CancelIoEx(GetStdHandle(STD_INPUT_HANDLE), IntPtr.Zero); } else { Console.WriteLine("\n时间到!进入下一题"); } // 收尾倒计时任务,避免未处理的取消异常 try { await countdownTask; } catch (OperationCanceledException) { } } } static async Task DisplayCountdownAsync(CancellationToken token) { for (int i = 10; i > 0; i--) { token.ThrowIfCancellationRequested(); // 用\r回到行首,实现倒计时数字实时刷新 Console.Write($"\r{i} 秒剩余,请输入答案:"); // 传入token,确保Delay能被及时取消 await Task.Delay(1000, token); } token.ThrowIfCancellationRequested(); Console.Write("\r0 秒剩余,请输入答案:"); } }
关键解决要点
- 异步主线程:使用
async Task Main简化异步任务的等待和管理,替代原来的同步阻塞逻辑。 - CancellationToken联动:通过
CancellationTokenSource实现倒计时和输入任务的双向取消——用户输入后立即终止倒计时,倒计时结束后终止输入等待。 - 强制终止控制台输入:调用
CancelIoEx中断Console.ReadLine()的阻塞状态,这是解决你之前Task.WhenAny后输入仍卡住的核心。 - 实时倒计时显示:利用
\r回车符回到当前行开头,覆盖原有内容实现倒计时数字的实时刷新,避免产生冗余输出。
原代码的问题分析
- 原方案仅用
Task.Delay+ContinueWith实现超时,但没有处理任务间的取消联动,也没有实现实时倒计时的UI反馈。 - 直接使用
Task.WhenAny后未强制终止Console.ReadLine的阻塞,导致它会持续占用输入资源,影响下一个问题的流程。
内容的提问来源于stack exchange,提问作者Klagefurt
相关产品推荐
相关产品推荐

