답변:
5 년이 지난 후에도 모든 답변이 여전히 다음 문제 중 하나 이상을 겪고 있다는 사실에 놀랐습니다.
내 솔루션이 위의 문제로 고통받지 않고 원래 문제를 해결할 것이라고 믿습니다.
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
하고 시간 제한을 생략하여 기본값이 무한 시간 제한으로 설정되도록합니다.
그렇다면 제가 언급 한 다른 솔루션의 문제는 어떻습니까?
이 솔루션으로 예상되는 유일한 문제는 스레드로부터 안전하지 않다는 것입니다. 그러나 여러 스레드는 실제로 사용자에게 동시에 입력을 요청할 수 없으므로 Reader.ReadLine
어쨌든 호출하기 전에 동기화가 이루어져야 합니다.
horrible waste
하지만 물론 신호가 우수합니다. 또한 Console.ReadLine
두 번째 위협에서 무한 루프에서 하나의 차단 호출을 사용 하면 아래의 다른 솔루션과 같이 백그라운드에서 이러한 호출이 많이 발생하는 문제를 방지 할 수 있습니다. 코드를 공유해 주셔서 감사합니다. +1
Console.ReadLine()
호출 에서 중단되는 것 같습니다 . ReadLine
먼저 완료해야하는 "팬텀" 이 표시됩니다.
getInput
.
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();
ReadLine
전화는 입력을 기다리고 있습니다. 100 번 호출하면 Enter 키를 100 번 누를 때까지 모두 사라지지 않는 100 개의 스레드가 생성됩니다!
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);
}
}
KeyAvailable
사용자가 ReadLine에 입력을 입력하기 시작했음을 나타내지 만 Enter 키를 누르면 이벤트가 필요하므로 ReadLine이 반환됩니다. 이 솔루션은 ReadKey에서만 작동합니다. 즉, 하나의 문자 만 가져옵니다. 이것은 ReadLine에 대한 실제 질문을 해결하지 못하므로 귀하의 솔루션을 사용할 수 없습니다. -1 죄송합니다
이것은 나를 위해 일했습니다.
ConsoleKeyInfo k = new ConsoleKeyInfo();
Console.WriteLine("Press any key in the next 5 seconds.");
for (int cnt = 5; cnt > 0; cnt--)
{
if (Console.KeyAvailable)
{
k = Console.ReadKey();
break;
}
else
{
Console.WriteLine(cnt.ToString());
System.Threading.Thread.Sleep(1000);
}
}
Console.WriteLine("The key pressed was " + k.Key);
어떤 식 으로든 두 번째 스레드가 필요합니다. 비동기 IO를 사용하여 자체 선언을 피할 수 있습니다.
읽기가 데이터를 반환하는 경우 이벤트를 설정하면 기본 스레드가 계속됩니다. 그렇지 않으면 시간 초과 후에도 계속됩니다.
// 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.
}
보조 스레드를 만들고 콘솔에서 키를 폴링해야한다고 생각합니다. 나는 이것을 달성하는 방법이 없다는 것을 알고 있습니다.
기업 환경에서 완벽하게 작동하는 솔루션을 찾기 전에 5 개월 동안이 문제로 고생했습니다.
지금까지 대부분의 솔루션의 문제점은 Console.ReadLine () 이외의 다른 것에 의존하고 있으며 Console.ReadLine ()에는 많은 이점이 있다는 것입니다.
내 솔루션은 다음과 같습니다.
샘플 코드 :
InputSimulator.SimulateKeyPress(VirtualKeyCode.RETURN);
Console.ReadLine을 사용하는 스레드를 중단하는 올바른 기술을 포함하여이 기술에 대한 추가 정보 :
사용자가 'enter'를 누르지 않으면 해당 호출이 반환되지 않기 때문에 대리자에서 Console.ReadLine ()을 호출하는 것은 좋지 않습니다. 델리게이트를 실행하는 스레드는 사용자가 'enter'를 누를 때까지 차단되며 취소 할 방법이 없습니다.
이러한 호출의 시퀀스를 발행하면 예상대로 작동하지 않습니다. 다음을 고려하십시오 (위의 예제 콘솔 클래스 사용).
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 호출은 계속 차단되고 시간 초과는 결국 만료되며 반환 된 값이 다시 기본값이됩니다.
BTW- 위 코드에 버그가 있습니다. waitHandle.Close ()를 호출하여 작업자 스레드 아래에서 이벤트를 닫습니다. 타임 아웃이 만료 된 후 사용자가 'enter'를 누르면 작업자 스레드는 ObjectDisposedException을 발생시키는 이벤트에 신호를 보내려고합니다. 예외는 작업자 스레드에서 발생하며 처리되지 않은 예외 처리기를 설정하지 않은 경우 프로세스가 종료됩니다.
방법에 있다면을 Main()
사용할 수 await
없으므로 다음을 사용해야합니다 Task.WaitAny()
.
var task = Task.Factory.StartNew(Console.ReadLine);
var result = Task.WaitAny(new Task[] { task }, TimeSpan.FromSeconds(5)) == 0
? task.Result : string.Empty;
그러나 C # 7.1에는 비동기 Main()
메서드 를 만들 수있는 가능성이 있으므로 Task.WhenAny()
해당 옵션이있을 때마다 버전 을 사용하는 것이 좋습니다.
var task = Task.Factory.StartNew(Console.ReadLine);
var completedTask = await Task.WhenAny(task, Task.Delay(TimeSpan.FromSeconds(5)));
var result = object.ReferenceEquals(task, completedTask) ? task.Result : string.Empty;
질문을 너무 많이 읽고 있을지 모르지만, 키를 누르지 않으면 15 초 동안 대기하는 부팅 메뉴와 비슷할 것이라고 가정합니다. (1) 차단 기능을 사용하거나 (2) 스레드, 이벤트 및 타이머를 사용할 수 있습니다. 이벤트는 '계속'역할을하며 타이머가 만료되거나 키를 누를 때까지 차단됩니다.
(1)에 대한 의사 코드는 다음과 같습니다.
// Get configurable wait time
TimeSpan waitTime = TimeSpan.FromSeconds(15.0);
int configWaitTimeSec;
if (int.TryParse(ConfigManager.AppSetting["DefaultWaitTime"], out configWaitTimeSec))
waitTime = TimeSpan.FromSeconds(configWaitTimeSec);
bool keyPressed = false;
DateTime expireTime = DateTime.Now + waitTime;
// Timer and key processor
ConsoleKeyInfo cki;
// EDIT: adding a missing ! below
while (!keyPressed && (DateTime.Now < expireTime))
{
if (Console.KeyAvailable)
{
cki = Console.ReadKey(true);
// TODO: Process key
keyPressed = true;
}
Thread.Sleep(10);
}
불행히도 Gulzar의 게시물에 대해 언급 할 수는 없지만 여기에 더 자세한 예가 있습니다.
while (Console.KeyAvailable == false)
{
Thread.Sleep(250);
i++;
if (i > 3)
throw new Exception("Timedout waiting for input.");
}
input = Console.ReadLine();
편집 : 실제 작업을 별도의 프로세스에서 수행하고 시간이 초과되면 해당 프로세스를 종료하여 문제를 해결했습니다. 자세한 내용은 아래를 참조하십시오. 아휴!
이것을 실행하고 잘 작동하는 것처럼 보였습니다. 내 동료는 Thread 객체를 사용하는 버전을 가지고 있지만 델리게이트 유형의 BeginInvoke () 메서드가 좀 더 우아하다는 것을 알았습니다.
namespace TimedReadLine
{
public static class Console
{
private delegate string ReadLineInvoker();
public static string ReadLine(int timeout)
{
return ReadLine(timeout, null);
}
public static string ReadLine(int timeout, string @default)
{
using (var process = new System.Diagnostics.Process
{
StartInfo =
{
FileName = "ReadLine.exe",
RedirectStandardOutput = true,
UseShellExecute = false
}
})
{
process.Start();
var rli = new ReadLineInvoker(process.StandardOutput.ReadLine);
var iar = rli.BeginInvoke(null, null);
if (!iar.AsyncWaitHandle.WaitOne(new System.TimeSpan(0, 0, timeout)))
{
process.Kill();
return @default;
}
return rli.EndInvoke(iar);
}
}
}
}
ReadLine.exe 프로젝트는 다음과 같은 하나의 클래스가있는 매우 간단한 프로젝트입니다.
namespace ReadLine
{
internal static class Program
{
private static void Main()
{
System.Console.WriteLine(System.Console.ReadLine());
}
}
}
Console.ReadLine()
이 차단되고 다음 요청에서 입력을 보류 한다는 사실에 매달립니다 . 허용되는 답변은 상당히 비슷하지만 여전히 제한이 있습니다.
ReadLine()
이것을 호출 한 후 프로그램에 다른 것이 있습니다. 무슨 일이 일어나는지보십시오. .NET Framework의 단일 스레드 특성으로 인해 돌아가려면 return 키를 두 번 눌러야합니다 Console
. 그것. 그렇지 않습니다. 작업.
.NET 4는 Tasks를 사용하여 매우 간단하게 만듭니다.
먼저 도우미를 만드세요.
Private Function AskUser() As String
Console.Write("Answer my question: ")
Return Console.ReadLine()
End Function
둘째, 작업으로 실행하고 기다립니다.
Dim askTask As Task(Of String) = New TaskFactory().StartNew(Function() AskUser())
askTask.Wait(TimeSpan.FromSeconds(30))
If Not askTask.IsCompleted Then
Console.WriteLine("User failed to respond.")
Else
Console.WriteLine(String.Format("You responded, '{0}'.", askTask.Result))
End If
ReadLine 기능을 다시 만들거나이 작업을 수행하기 위해 다른 위험한 해킹을 수행하려는 시도가 없습니다. 작업을 통해 매우 자연스러운 방식으로 문제를 해결할 수 있습니다.
여기에 충분한 답변이없는 것처럼 : 0), 다음은 위의 정적 메서드 @kwl의 솔루션 (첫 번째 솔루션)으로 캡슐화됩니다.
public static string ConsoleReadLineWithTimeout(TimeSpan timeout)
{
Task<string> task = Task.Factory.StartNew(Console.ReadLine);
string result = Task.WaitAny(new Task[] { task }, timeout) == 0
? task.Result
: string.Empty;
return result;
}
용법
static void Main()
{
Console.WriteLine("howdy");
string result = ConsoleReadLineWithTimeout(TimeSpan.FromSeconds(8.5));
Console.WriteLine("bye");
}
이를 해결하기위한 간단한 스레딩 예제
Thread readKeyThread = new Thread(ReadKeyMethod);
static ConsoleKeyInfo cki = null;
void Main()
{
readKeyThread.Start();
bool keyEntered = false;
for(int ii = 0; ii < 10; ii++)
{
Thread.Sleep(1000);
if(readKeyThread.ThreadState == ThreadState.Stopped)
keyEntered = true;
}
if(keyEntered)
{ //do your stuff for a key entered
}
}
void ReadKeyMethod()
{
cki = Console.ReadKey();
}
또는 전체 라인을 얻기 위해 상단에 정적 문자열.
내 경우에는 잘 작동합니다.
public static ManualResetEvent evtToWait = new ManualResetEvent(false);
private static void ReadDataFromConsole( object state )
{
Console.WriteLine("Enter \"x\" to exit or wait for 5 seconds.");
while (Console.ReadKey().KeyChar != 'x')
{
Console.Out.WriteLine("");
Console.Out.WriteLine("Enter again!");
}
evtToWait.Set();
}
static void Main(string[] args)
{
Thread status = new Thread(ReadDataFromConsole);
status.Start();
evtToWait = new ManualResetEvent(false);
evtToWait.WaitOne(5000); // wait for evtToWait.Set() or timeOut
status.Abort(); // exit anyway
return;
}
멋지고 짧지 않나요?
if (SpinWait.SpinUntil(() => Console.KeyAvailable, millisecondsTimeout))
{
ConsoleKeyInfo keyInfo = Console.ReadKey();
// Handle keyInfo value here...
}
이것은 Glen Slayden 솔루션의 완전한 예입니다. 나는 다른 문제에 대한 테스트 케이스를 만들 때 이것을 만들었습니다. 비동기 I / O 및 수동 재설정 이벤트를 사용합니다.
public static void Main() {
bool readInProgress = false;
System.IAsyncResult result = null;
var stop_waiting = new System.Threading.ManualResetEvent(false);
byte[] buffer = new byte[256];
var s = System.Console.OpenStandardInput();
while (true) {
if (!readInProgress) {
readInProgress = true;
result = s.BeginRead(buffer, 0, buffer.Length
, ar => stop_waiting.Set(), null);
}
bool signaled = true;
if (!result.IsCompleted) {
stop_waiting.Reset();
signaled = stop_waiting.WaitOne(5000);
}
else {
signaled = true;
}
if (signaled) {
readInProgress = false;
int numBytes = s.EndRead(result);
string text = System.Text.Encoding.UTF8.GetString(buffer
, 0, numBytes);
System.Console.Out.Write(string.Format(
"Thank you for typing: {0}", text));
}
else {
System.Console.Out.WriteLine("oy, type something!");
}
}
내 코드는 전적으로 친구의 답변 @JSQuareD를 기반으로합니다.
그러나 나는 Stopwatch
프로그램을 마쳤을 때 Console.ReadKey()
여전히 기다리고 있었고 Console.ReadLine()
예기치 않은 동작이 발생 했기 때문에 타이머 를 사용해야 했습니다.
그것은 나를 위해 완벽하게 작동했습니다. 원래 Console.ReadLine ()을 유지합니다.
class Program
{
static void Main(string[] args)
{
Console.WriteLine("What is the answer? (5 secs.)");
try
{
var answer = ConsoleReadLine.ReadLine(5000);
Console.WriteLine("Answer is: {0}", answer);
}
catch
{
Console.WriteLine("No answer");
}
Console.ReadKey();
}
}
class ConsoleReadLine
{
private static string inputLast;
private static Thread inputThread = new Thread(inputThreadAction) { IsBackground = true };
private static AutoResetEvent inputGet = new AutoResetEvent(false);
private static AutoResetEvent inputGot = new AutoResetEvent(false);
static ConsoleReadLine()
{
inputThread.Start();
}
private static void inputThreadAction()
{
while (true)
{
inputGet.WaitOne();
inputLast = Console.ReadLine();
inputGot.Set();
}
}
// omit the parameter to read a line without a timeout
public static string ReadLine(int timeout = Timeout.Infinite)
{
if (timeout == Timeout.Infinite)
{
return Console.ReadLine();
}
else
{
var stopwatch = new Stopwatch();
stopwatch.Start();
while (stopwatch.ElapsedMilliseconds < timeout && !Console.KeyAvailable) ;
if (Console.KeyAvailable)
{
inputGet.Set();
inputGot.WaitOne();
return inputLast;
}
else
{
throw new TimeoutException("User did not provide input within the timelimit.");
}
}
}
}
위의 Eric의 게시물 구현 예. 이 특정 예제는 파이프를 통해 콘솔 앱에 전달 된 정보를 읽는 데 사용되었습니다.
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
namespace PipedInfo
{
class Program
{
static void Main(string[] args)
{
StreamReader buffer = ReadPipedInfo();
Console.WriteLine(buffer.ReadToEnd());
}
#region ReadPipedInfo
public static StreamReader ReadPipedInfo()
{
//call with a default value of 5 milliseconds
return ReadPipedInfo(5);
}
public static StreamReader ReadPipedInfo(int waitTimeInMilliseconds)
{
//allocate the class we're going to callback to
ReadPipedInfoCallback callbackClass = new ReadPipedInfoCallback();
//to indicate read complete or timeout
AutoResetEvent readCompleteEvent = new AutoResetEvent(false);
//open the StdIn so that we can read against it asynchronously
Stream stdIn = Console.OpenStandardInput();
//allocate a one-byte buffer, we're going to read off the stream one byte at a time
byte[] singleByteBuffer = new byte[1];
//allocate a list of an arbitary size to store the read bytes
List<byte> byteStorage = new List<byte>(4096);
IAsyncResult asyncRead = null;
int readLength = 0; //the bytes we have successfully read
do
{
//perform the read and wait until it finishes, unless it's already finished
asyncRead = stdIn.BeginRead(singleByteBuffer, 0, singleByteBuffer.Length, new AsyncCallback(callbackClass.ReadCallback), readCompleteEvent);
if (!asyncRead.CompletedSynchronously)
readCompleteEvent.WaitOne(waitTimeInMilliseconds);
//end the async call, one way or another
//if our read succeeded we store the byte we read
if (asyncRead.IsCompleted)
{
readLength = stdIn.EndRead(asyncRead);
if (readLength > 0)
byteStorage.Add(singleByteBuffer[0]);
}
} while (asyncRead.IsCompleted && readLength > 0);
//we keep reading until we fail or read nothing
//return results, if we read zero bytes the buffer will return empty
return new StreamReader(new MemoryStream(byteStorage.ToArray(), 0, byteStorage.Count));
}
private class ReadPipedInfoCallback
{
public void ReadCallback(IAsyncResult asyncResult)
{
//pull the user-defined variable and strobe the event, the read finished successfully
AutoResetEvent readCompleteEvent = asyncResult.AsyncState as AutoResetEvent;
readCompleteEvent.Set();
}
}
#endregion ReadPipedInfo
}
}
string readline = "?";
ThreadPool.QueueUserWorkItem(
delegate
{
readline = Console.ReadLine();
}
);
do
{
Thread.Sleep(100);
} while (readline == "?");
"Console.ReadKey"경로로 이동하면 ReadLine의 멋진 기능 중 일부를 잃게됩니다.
시간 제한을 추가하려면 while 루프를 적절하게 변경하십시오.
과다한 기존 답변에 다른 솔루션을 추가하는 것을 싫어하지 마십시오! 이것은 Console.ReadKey ()에서 작동하지만 ReadLine () 등과 함께 작동하도록 쉽게 수정할 수 있습니다.
"Console.Read"메소드가 차단 중이므로 읽기를 취소하려면 StdIn 스트림을 " 조금씩 이동 "해야합니다.
호출 구문 :
ConsoleKeyInfo keyInfo;
bool keyPressed = AsyncConsole.ReadKey(500, out keyInfo);
// where 500 is the timeout
암호:
public class AsyncConsole // not thread safe
{
private static readonly Lazy<AsyncConsole> Instance =
new Lazy<AsyncConsole>();
private bool _keyPressed;
private ConsoleKeyInfo _keyInfo;
private bool DoReadKey(
int millisecondsTimeout,
out ConsoleKeyInfo keyInfo)
{
_keyPressed = false;
_keyInfo = new ConsoleKeyInfo();
Thread readKeyThread = new Thread(ReadKeyThread);
readKeyThread.IsBackground = false;
readKeyThread.Start();
Thread.Sleep(millisecondsTimeout);
if (readKeyThread.IsAlive)
{
try
{
IntPtr stdin = GetStdHandle(StdHandle.StdIn);
CloseHandle(stdin);
readKeyThread.Join();
}
catch { }
}
readKeyThread = null;
keyInfo = _keyInfo;
return _keyPressed;
}
private void ReadKeyThread()
{
try
{
_keyInfo = Console.ReadKey();
_keyPressed = true;
}
catch (InvalidOperationException) { }
}
public static bool ReadKey(
int millisecondsTimeout,
out ConsoleKeyInfo keyInfo)
{
return Instance.Value.DoReadKey(millisecondsTimeout, out keyInfo);
}
private enum StdHandle { StdIn = -10, StdOut = -11, StdErr = -12 };
[DllImport("kernel32.dll")]
private static extern IntPtr GetStdHandle(StdHandle std);
[DllImport("kernel32.dll")]
private static extern bool CloseHandle(IntPtr hdl);
}
다음은 Console.KeyAvailable
. 이들은 호출을 차단하고 있지만 원하는 경우 TPL을 통해 비동기 적으로 호출하는 것은 매우 간단합니다. 저는 표준 취소 메커니즘을 사용하여 작업 비동기 패턴과 그 모든 좋은 것들에 쉽게 연결할 수 있도록했습니다.
public static class ConsoleEx
{
public static string ReadLine(TimeSpan timeout)
{
var cts = new CancellationTokenSource();
return ReadLine(timeout, cts.Token);
}
public static string ReadLine(TimeSpan timeout, CancellationToken cancellation)
{
string line = "";
DateTime latest = DateTime.UtcNow.Add(timeout);
do
{
cancellation.ThrowIfCancellationRequested();
if (Console.KeyAvailable)
{
ConsoleKeyInfo cki = Console.ReadKey();
if (cki.Key == ConsoleKey.Enter)
{
return line;
}
else
{
line += cki.KeyChar;
}
}
Thread.Sleep(1);
}
while (DateTime.UtcNow < latest);
return null;
}
}
이것에는 몇 가지 단점이 있습니다.
ReadLine
제공 하는 표준 탐색 기능 (위 / 아래 화살표 스크롤 등)을 사용할 수 없습니다.중복 질문이 있었기 때문에 여기로 끝났습니다. 나는 간단 해 보이는 다음 해결책을 생각 해냈다. 내가 놓친 몇 가지 단점이 있다고 확신합니다.
static void Main(string[] args)
{
Console.WriteLine("Hit q to continue or wait 10 seconds.");
Task task = Task.Factory.StartNew(() => loop());
Console.WriteLine("Started waiting");
task.Wait(10000);
Console.WriteLine("Stopped waiting");
}
static void loop()
{
while (true)
{
if ('q' == Console.ReadKey().KeyChar) break;
}
}
나는이 대답에 왔고 결국 :
/// <summary>
/// Reads Line from console with timeout.
/// </summary>
/// <exception cref="System.TimeoutException">If user does not enter line in the specified time.</exception>
/// <param name="timeout">Time to wait in milliseconds. Negative value will wait forever.</param>
/// <returns></returns>
public static string ReadLine(int timeout = -1)
{
ConsoleKeyInfo cki = new ConsoleKeyInfo();
StringBuilder sb = new StringBuilder();
// if user does not want to spesify a timeout
if (timeout < 0)
return Console.ReadLine();
int counter = 0;
while (true)
{
while (Console.KeyAvailable == false)
{
counter++;
Thread.Sleep(1);
if (counter > timeout)
throw new System.TimeoutException("Line was not entered in timeout specified");
}
cki = Console.ReadKey(false);
if (cki.Key == ConsoleKey.Enter)
{
Console.WriteLine();
return sb.ToString();
}
else
sb.Append(cki.KeyChar);
}
}
훨씬 더 현대적이고 작업 기반 코드는 다음과 같습니다.
public string ReadLine(int timeOutMillisecs)
{
var inputBuilder = new StringBuilder();
var task = Task.Factory.StartNew(() =>
{
while (true)
{
var consoleKey = Console.ReadKey(true);
if (consoleKey.Key == ConsoleKey.Enter)
{
return inputBuilder.ToString();
}
inputBuilder.Append(consoleKey.KeyChar);
}
});
var success = task.Wait(timeOutMillisecs);
if (!success)
{
throw new TimeoutException("User did not provide input within the timelimit.");
}
return inputBuilder.ToString();
}
Windows 응용 프로그램 (Windows 서비스)이있는 독특한 상황이있었습니다. 프로그램을 대화 형으로 실행할 때 Environment.IsInteractive
(VS Debugger 또는 cmd.exe에서) AttachConsole / AllocConsole을 사용하여 stdin / stdout을 가져 왔습니다. 작업이 수행되는 동안 프로세스가 종료되지 않도록 UI 스레드는 Console.ReadKey(false)
. UI 스레드가 다른 스레드에서 수행하는 대기를 취소하고 싶었 기 때문에 @JSquaredD로 솔루션을 수정했습니다.
using System;
using System.Diagnostics;
internal class PressAnyKey
{
private static Thread inputThread;
private static AutoResetEvent getInput;
private static AutoResetEvent gotInput;
private static CancellationTokenSource cancellationtoken;
static PressAnyKey()
{
// Static Constructor called when WaitOne is called (technically Cancel too, but who cares)
getInput = new AutoResetEvent(false);
gotInput = new AutoResetEvent(false);
inputThread = new Thread(ReaderThread);
inputThread.IsBackground = true;
inputThread.Name = "PressAnyKey";
inputThread.Start();
}
private static void ReaderThread()
{
while (true)
{
// ReaderThread waits until PressAnyKey is called
getInput.WaitOne();
// Get here
// Inner loop used when a caller uses PressAnyKey
while (!Console.KeyAvailable && !cancellationtoken.IsCancellationRequested)
{
Thread.Sleep(50);
}
// Release the thread that called PressAnyKey
gotInput.Set();
}
}
/// <summary>
/// Signals the thread that called WaitOne should be allowed to continue
/// </summary>
public static void Cancel()
{
// Trigger the alternate ending condition to the inner loop in ReaderThread
if(cancellationtoken== null) throw new InvalidOperationException("Must call WaitOne before Cancelling");
cancellationtoken.Cancel();
}
/// <summary>
/// Wait until a key is pressed or <see cref="Cancel"/> is called by another thread
/// </summary>
public static void WaitOne()
{
if(cancellationtoken==null || cancellationtoken.IsCancellationRequested) throw new InvalidOperationException("Must cancel a pending wait");
cancellationtoken = new CancellationTokenSource();
// Release the reader thread
getInput.Set();
// Calling thread will wait here indefiniately
// until a key is pressed, or Cancel is called
gotInput.WaitOne();
}
}