Sono sorpreso di apprendere che dopo 5 anni tutte le risposte soffrono ancora di uno o più dei seguenti problemi:
- Viene utilizzata una funzione diversa da ReadLine, che causa la perdita di funzionalità. (Canc / backspace / tasto su per l'immissione precedente).
- La funzione si comporta male quando viene richiamata più volte (generazione di più thread, molti ReadLine sospesi o comportamento imprevisto).
- La funzione si basa su un'attesa impegnata. Il che è uno spreco orribile poiché l'attesa dovrebbe durare da un numero di secondi fino al timeout, che potrebbe essere di più minuti. Un'attesa impegnativa che dura per una tale quantità di tempo è un orribile risucchio di risorse, il che è particolarmente negativo in uno scenario di multithreading. Se l'attesa occupata viene modificata con uno sleep, ciò ha un effetto negativo sulla reattività, anche se ammetto che questo probabilmente non è un grosso problema.
Credo che la mia soluzione risolverà il problema originale senza soffrire di nessuno dei problemi di cui sopra:
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.");
}
}
La chiamata è, ovviamente, molto semplice:
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.");
}
In alternativa, puoi usare la TryXX(out)
convenzione, come suggerito da 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;
}
Che si chiama come segue:
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);
In entrambi i casi, non è possibile combinare chiamate Reader
con Console.ReadLine
chiamate normali : se Reader
scade, ci sarà una ReadLine
chiamata in sospeso . Invece, se vuoi avere una ReadLine
chiamata normale (non a tempo) , usa semplicemente Reader
e ometti il timeout, in modo che il valore predefinito sia un timeout infinito.
Allora che ne dici di quei problemi delle altre soluzioni che ho menzionato?
- Come puoi vedere, viene utilizzato ReadLine, evitando il primo problema.
- La funzione si comporta correttamente se richiamata più volte. Indipendentemente dal fatto che si verifichi o meno un timeout, solo un thread in background sarà sempre in esecuzione e solo una chiamata a ReadLine sarà sempre attiva. La chiamata alla funzione risulterà sempre nell'ultimo input o in un timeout e l'utente non dovrà premere invio più di una volta per inviare il suo input.
- E, ovviamente, la funzione non si basa su un'attesa impegnativa. Utilizza invece tecniche di multithreading adeguate per evitare sprechi di risorse.
L'unico problema che prevedo con questa soluzione è che non è thread-safe. Tuttavia, più thread non possono realmente chiedere l'input all'utente contemporaneamente, quindi la sincronizzazione dovrebbe avvenire comunque prima di effettuare una chiamata a Reader.ReadLine
.