JSQ*_*reD 107
我很惊讶地发现,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.");
}
}
Run Code Online (Sandbox Code Playgroud)
当然,通话非常简单:
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.");
}
Run Code Online (Sandbox Code Playgroud)
或者,您可以使用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;
}
Run Code Online (Sandbox Code Playgroud)
其名称如下:
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);
Run Code Online (Sandbox Code Playgroud)
在这两种情况下,您都无法将呼叫Reader与正常Console.ReadLine呼叫混合:如果Reader超时,则会有挂机ReadLine呼叫.相反,如果您想要进行正常(非定时)ReadLine调用,只需使用Reader并省略超时,以便默认为无限超时.
那么我提到的其他解决方案的那些问题呢?
我预见到这个解决方案的唯一问题是它不是线程安全的.但是,多个线程无法真正要求用户同时输入,因此Reader.ReadLine无论如何都应该在进行呼叫之前进行同步.
gp.*_*gp. 33
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();
Run Code Online (Sandbox Code Playgroud)
Gul*_*zim 27
这种方法会使用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);
}
}
Run Code Online (Sandbox Code Playgroud)
Eri*_*ric 10
不管怎样,你需要第二个线程.您可以使用异步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.
}
Run Code Online (Sandbox Code Playgroud)
小智 9
这对我有用.
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);
Run Code Online (Sandbox Code Playgroud)
在我找到一个在企业环境中完美运行的解决方案之前,我在这个问题上挣扎了5个月.
到目前为止,大多数解决方案的问题在于它们依赖于Console.ReadLine()之外的其他东西,并且Console.ReadLine()具有很多优点:
我的解决方案如下:
示例代码:
InputSimulator.SimulateKeyPress(VirtualKeyCode.RETURN);
Run Code Online (Sandbox Code Playgroud)
有关此技术的更多信息,包括中止使用Console.ReadLine的线程的正确技术:
.NET调用将[enter]键发送到当前进程,这是一个控制台应用程序?
当所述线程正在执行Console.ReadLine时,如何在.NET中中止另一个线程?
如果您在该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;
Run Code Online (Sandbox Code Playgroud)
但是,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;
Run Code Online (Sandbox Code Playgroud)
在委托中调用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");
Run Code Online (Sandbox Code Playgroud)
用户让第一个提示的超时时间到期,然后为第二个提示输入一个值。firstName和lastName都将包含默认值。当用户单击“ enter”时,将完成第一个 ReadLine调用,但是代码已放弃该调用,并实际上丢弃了结果。在第二的ReadLine调用将继续阻止,超时最终将到期,返回的值将再次成为默认。
顺便说一句-上面的代码中有一个错误。通过调用waitHandle.Close(),可以从工作线程下关闭事件。如果用户在超时到期后单击“ enter”,则工作线程将尝试发信号通知引发ObjectDisposedException的事件。异常是从工作线程中抛出的,如果您尚未设置未处理的异常处理程序,则该过程将终止。
| 归档时间: |
|
| 查看次数: |
52944 次 |
| 最近记录: |