Console.ReadLine() にタイムアウトを追加するにはどうすればいいですか? 質問する

Console.ReadLine() にタイムアウトを追加するにはどうすればいいですか? 質問する

ユーザーに提供したいコンソールアプリがありますバツプロンプトに応答するには 1 秒かかります。一定時間経過しても入力がない場合、プログラム ロジックは続行されます。タイムアウトは空の応答を意味するものと想定します。

これに取り組む最も簡単な方法は何でしょうか?

ベストアンサー1

5 年経った今でも、すべての回答に以下の問題の 1 つ以上が残っていることに驚きました。

  • ReadLine 以外の関数が使用されているため、機能が失われます。(前の入力には Delete/Backspace/Up キーを使用します)。
  • 関数は、複数回呼び出されると不正な動作をします (複数のスレッドが生成されたり、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 が使用され、最初の問題は回避されます。
  • この関数は、複数回呼び出されても適切に動作します。タイムアウトが発生するかどうかに関係なく、バックグラウンド スレッドは常に 1 つだけ実行され、ReadLine への呼び出しは最大でも 1 つだけアクティブになります。この関数を呼び出すと、常に最新の入力が使用されるか、タイムアウトが発生し、ユーザーは入力を送信するために Enter キーを複数回押す必要がなくなります。
  • そして、明らかに、この関数はビジー待機に依存していません。代わりに、リソースの浪費を防ぐために適切なマルチスレッド技術を使用しています。

このソリューションで予測される唯一の問題は、スレッドセーフではないことです。ただし、複数のスレッドが同時にユーザーに入力を求めることはできないため、いずれにReader.ReadLineせよ、呼び出しを行う前に同期が行われる必要があります。

おすすめ記事