Me sorprende saber que después de 5 años, todas las respuestas aún sufren uno o más de los siguientes problemas:
- Se utiliza una función distinta de ReadLine, que causa la pérdida de funcionalidad. (Eliminar / retroceso / tecla arriba para la entrada anterior).
- La función se comporta mal cuando se invoca varias veces (genera múltiples subprocesos, muchos ReadLine colgantes o comportamientos inesperados).
- La función se basa en una espera ocupada. Lo cual es un desperdicio horrible ya que se espera que la espera se ejecute en cualquier lugar desde una cantidad de segundos hasta el tiempo de espera, que puede ser de varios minutos. Una espera ocupada que dura tanto tiempo es una horrible cantidad de recursos, lo que es especialmente malo en un escenario de subprocesos múltiples. Si la espera ocupada se modifica con un sueño, esto tiene un efecto negativo en la capacidad de respuesta, aunque admito que esto probablemente no sea un gran problema.
Creo que mi solución resolverá el problema original sin sufrir ninguno de los problemas anteriores:
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.");
}
}
Llamar es, por supuesto, muy fácil:
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.");
}
Alternativamente, puede usar la TryXX(out)
convención, como sugirió 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;
}
Que se llama de la siguiente manera:
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);
En ambos casos, no puede mezclar llamadas Reader
con Console.ReadLine
llamadas normales : si se Reader
agota el tiempo de espera, habrá una ReadLine
llamada pendiente . En cambio, si desea tener una ReadLine
llamada normal (no programada) , simplemente use Reader
y omita el tiempo de espera, de modo que el valor predeterminado sea un tiempo de espera infinito.
Entonces, ¿qué hay de esos problemas de las otras soluciones que mencioné?
- Como puede ver, se usa ReadLine, evitando el primer problema.
- La función se comporta correctamente cuando se invoca varias veces. Independientemente de si se produce un tiempo de espera o no, solo se ejecutará un subproceso en segundo plano y solo como máximo una llamada a ReadLine estará activa. Llamar a la función siempre dará como resultado la última entrada o un tiempo de espera, y el usuario no tendrá que presionar enter más de una vez para enviar su entrada.
- Y, obviamente, la función no se basa en una espera ocupada. En su lugar, utiliza técnicas adecuadas de subprocesamiento múltiple para evitar el desperdicio de recursos.
El único problema que preveo con esta solución es que no es seguro para subprocesos. Sin embargo, varios subprocesos realmente no pueden pedirle al usuario que ingrese al mismo tiempo, por lo que la sincronización debería estar ocurriendo antes de hacer una llamada Reader.ReadLine
.