为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

炎炎设计 2019-06-29 10:19:01
如何向Console.ReadLine()添加超时?我有一个控制台应用程序,我想在其中给用户x秒来响应提示符。如果在一段时间后没有输入,则程序逻辑应该继续。我们假设超时意味着空响应。最直截了当的方法是什么?
查看完整描述

3 回答

?
MM们

TA贡献1886条经验 获得超2个赞

我惊讶地获悉,五年后,所有答案仍然存在以下一个或多个问题:

  • 使用的是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的调用将是活动的。调用函数总是会导致最新的输入,或者超时,用户不必多次按Enter键才能提交输入。
  • 而且,很明显,这个函数不依赖于繁忙的等待。相反,它使用适当的多线程技术来防止资源浪费。

我认为这个解决方案唯一的问题是它不是线程安全的。但是,多个线程实际上不能同时请求用户输入,因此在调用Reader.ReadLine不管怎么说。


查看完整回答
反对 回复 2019-06-29
?
达令说

TA贡献1821条经验 获得超6个赞

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();


查看完整回答
反对 回复 2019-06-29
?
开心每一天1111

TA贡献1836条经验 获得超13个赞

这种方法会使用Consol.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);
    }}


查看完整回答
反对 回复 2019-06-29
  • 3 回答
  • 0 关注
  • 1228 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信