当前位置:  开发笔记 > 编程语言 > 正文

如何将超时添加到Console.ReadLine()?

如何解决《如何将超时添加到Console.ReadLine()?》经验,为你挑选了9个好方法。

我有一个控制台应用程序,我想让用户x秒响应提示.如果在一段时间后没有输入,程序逻辑应该继续.我们假设超时意味着空响应.

接近这个的最直接的方法是什么?



1> JSQuareD..:

我很惊讶地发现,5年后,所有答案仍然存在以下一个或多个问题:

使用除ReadLine之外的功能,导致功能丧失.(删除/退格/上一次输入的上行键).

多次调用时函数表现不佳(产生多个线程,许多挂起ReadLine或其他意外行为).

功能依赖于忙碌等待.这是一个可怕的浪费,因为等待预计会从几秒到超时运行,这可能是多分钟.忙碌的等待会耗费大量时间,这是一种可怕的资源,在多线程场景中尤其糟糕.如果使用睡眠修改忙等待会对响应性产生负面影响,尽管我承认这可能不是一个大问题.

我相信我的解决方案将解决原始问题,而不会遇到任何上述问题:

class Reader {
  private static Thread inputThread;
  private static AutoResetEvent getInput, gotInput;
  private static string input;

  static Reader() {
    getInput = new AutoResetEvent(false);
    gotInput = new AutoResetEvent(false);
    inputThread = new Thread(reader);
    inputThread.IsBackground = true;
    inputThread.Start();
  }

  private static void reader() {
    while (true) {
      getInput.WaitOne();
      input = Console.ReadLine();
      gotInput.Set();
    }
  }

  // omit the parameter to read a line without a timeout
  public static string ReadLine(int timeOutMillisecs = Timeout.Infinite) {
    getInput.Set();
    bool success = gotInput.WaitOne(timeOutMillisecs);
    if (success)
      return input;
    else
      throw new TimeoutException("User did not provide input within the timelimit.");
  }
}

当然,通话非常简单:

try {
  Console.WriteLine("Please enter your name within the next 5 seconds.");
  string name = Reader.ReadLine(5000);
  Console.WriteLine("Hello, {0}!", name);
} catch (TimeoutException) {
  Console.WriteLine("Sorry, you waited too long.");
}

或者,您可以使用TryXX(out)约定,如shmueli建议:

  public static bool TryReadLine(out string line, int timeOutMillisecs = Timeout.Infinite) {
    getInput.Set();
    bool success = gotInput.WaitOne(timeOutMillisecs);
    if (success)
      line = input;
    else
      line = null;
    return success;
  }

其名称如下:

Console.WriteLine("Please enter your name within the next 5 seconds.");
string name;
bool success = Reader.TryReadLine(out name, 5000);
if (!success)
  Console.WriteLine("Sorry, you waited too long.");
else
  Console.WriteLine("Hello, {0}!", name);

在这两种情况下,您都无法将呼叫Reader与正常Console.ReadLine呼叫混合:如果Reader超时,则会有挂机ReadLine呼叫.相反,如果您想要进行正常(非定时)ReadLine调用,只需使用Reader并省略超时,以便默认为无限超时.

那么我提到的其他解决方案的那些问题呢?

如您所见,使用了ReadLine,避免了第一个问题.

多次调用时,该函数表现正常.无论是否发生超时,只有一个后台线程将运行,并且只有一次调用ReadLine将始终处于活动状态.调用该函数将始终导致最新输入或超时,并且用户不必多次输入以提交输入.

而且,显然,该功能不依赖于忙等待.相反,它使用适当的多线程技术来防止浪费资源.

我预见到这个解决方案的唯一问题是它不是线程安全的.但是,多个线程无法真正要求用户同时输入,因此Reader.ReadLine无论如何都应该在进行呼叫之前进行同步.


如果您未能及时输入,则此方法似乎在您进行的第一个后续“ Console.ReadLine()”调用中中断。最后,您需要先完成“幻像”`ReadLine`。

2> gp...:
string ReadLine(int timeoutms)
{
    ReadLineDelegate d = Console.ReadLine;
    IAsyncResult result = d.BeginInvoke(null, null);
    result.AsyncWaitHandle.WaitOne(timeoutms);//timeout e.g. 15000 for 15 secs
    if (result.IsCompleted)
    {
        string resultstr = d.EndInvoke(result);
        Console.WriteLine("Read: " + resultstr);
        return resultstr;
    }
    else
    {
        Console.WriteLine("Timed out!");
        throw new TimedoutException("Timed Out!");
    }
}

delegate string ReadLineDelegate();


@Gravitas:这不起作用.好吧,它工作一次.但是你调用的每个`ReadLine`都在那里等待输入.如果你调用它100次,它会创建100个线程,直到你按Enter键100次才会消失!
我不知道为什么没有投票 - 它完美无瑕.许多其他解决方案涉及"ReadKey()",它无法正常工作:它意味着您失去了ReadLine()的所有功能,例如按下"向上"键以获取以前键入的命令,使用退格键和箭头键等

3> Gulzar Nazim..:

这种方法会使用Console.KeyAvailable帮助吗?

class Sample 
{
    public static void Main() 
    {
    ConsoleKeyInfo cki = new ConsoleKeyInfo();

    do {
        Console.WriteLine("\nPress a key to display; press the 'x' key to quit.");

// Your code could perform some useful task in the following loop. However, 
// for the sake of this example we'll merely pause for a quarter second.

        while (Console.KeyAvailable == false)
            Thread.Sleep(250); // Loop until input is entered.
        cki = Console.ReadKey(true);
        Console.WriteLine("You pressed the '{0}' key.", cki.Key);
        } while(cki.Key != ConsoleKey.X);
    }
}



4> Eric..:

不管怎样,你需要第二个线程.您可以使用异步IO来避免声明自己的:

声明一个ManualResetEvent,称之为"evt"

调用System.Console.OpenStandardInput来获取输入流.指定将存储其数据并设置evt的回调方法.

调用该流的BeginRead方法来启动异步读取操作

然后在ManualResetEvent上输入定时等待

如果等待超时,则取消读取

如果读取返回数据,则设置事件并且主线程将继续,否则您将在超时后继续.



5> Glenn Slayde..:
// Wait for 'Enter' to be pressed or 5 seconds to elapse
using (Stream s = Console.OpenStandardInput())
{
    ManualResetEvent stop_waiting = new ManualResetEvent(false);
    s.BeginRead(new Byte[1], 0, 1, ar => stop_waiting.Set(), null);

    // ...do anything else, or simply...

    stop_waiting.WaitOne(5000);
    // If desired, other threads could also set 'stop_waiting' 
    // Disposing the stream cancels the async read operation. It can be
    // re-opened if needed.
}



6> 小智..:

这对我有用.

ConsoleKeyInfo k = new ConsoleKeyInfo();
Console.WriteLine("Press any key in the next 5 seconds.");
for (int cnt = 5; cnt > 0; cnt--)
  {
    if (Console.KeyAvailable == true)
      {
        k = Console.ReadKey();
        break;
      }
    else
     {
       Console.WriteLine(cnt.ToString());
       System.Threading.Thread.Sleep(1000);
     }
 }
Console.WriteLine("The key pressed was " + k.Key);


我认为这是使用已内置工具的最佳和最简单的解决方案.很好!

7> GEOCHET..:

我想你需要创建一个辅助线程并在控制台上轮询一个键.我知道没有内置的方法来实现这一目标.



8> Contango..:

在我找到一个在企业环境中完美运行的解决方案之前,我在这个问题上挣扎了5个月.

到目前为止,大多数解决方案的问题在于它们依赖于Console.ReadLine()之外的其他东西,并且Console.ReadLine()具有很多优点:

支持删除,退格,箭头键等

能够按下"向上"键并重复上一个命令(如果你实现了一个可以大量使用的后台调试控制台,这非常方便).

我的解决方案如下:

    使用Console.ReadLine()生成一个单独的线程来处理用户输入.

    在超时期限之后,通过使用http://inputsimulator.codeplex.com/将[enter]键发送到当前控制台窗口来解锁Console.ReadLine().

示例代码:

 InputSimulator.SimulateKeyPress(VirtualKeyCode.RETURN);

有关此技术的更多信息,包括中止使用Console.ReadLine的线程的正确技术:

.NET调用将[enter]键发送到当前进程,这是一个控制台应用程序?

当所述线程正在执行Console.ReadLine时,如何在.NET中中止另一个线程?



9> Brannon..:

在委托中调用Console.ReadLine()很糟糕,因为如果用户未点击“ enter”,则该调用将永远不会返回。执行委托的线程将被阻塞,直到用户单击“ enter”为止,而无法取消它。

发出一系列这些调用将不会像您期望的那样运行。考虑以下内容(使用上面的示例Console类):

System.Console.WriteLine("Enter your first name [John]:");

string firstName = Console.ReadLine(5, "John");

System.Console.WriteLine("Enter your last name [Doe]:");

string lastName = Console.ReadLine(5, "Doe");

用户让第一个提示的超时时间到期,然后为第二个提示输入一个值。firstName和lastName都将包含默认值。当用户单击“ enter”时,将完成第一个 ReadLine调用,但是代码已放弃该调用,并实际上丢弃了结果。在第二的ReadLine调用将继续阻止,超时最终将到期,返回的值将再次成为默认。

顺便说一句-上面的代码中有一个错误。通过调用waitHandle.Close(),可以从工作线程下关闭事件。如果用户在超时到期后单击“ enter”,则工作线程将尝试发信号通知引发ObjectDisposedException的事件。异常是从工作线程中抛出的,如果您尚未设置未处理的异常处理程序,则该过程将终止。

推荐阅读
ifx0448363
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有