Je suis surpris d'apprendre qu'après 5 ans, toutes les réponses souffrent encore d'un ou plusieurs des problèmes suivants:
- Une fonction autre que ReadLine est utilisée, entraînant une perte de fonctionnalité. (Effacer / retour arrière / touche haut pour l'entrée précédente).
- La fonction se comporte mal lorsqu'elle est appelée plusieurs fois (engendrant plusieurs threads, de nombreuses ReadLine suspendues ou un comportement inattendu).
- La fonction repose sur une attente occupée. Ce qui est un gaspillage horrible car on s'attend à ce que l'attente dure de quelques secondes jusqu'au délai d'expiration, qui peut durer plusieurs minutes. Une attente chargée qui dure pendant une telle quantité de temps est une horrible perte de ressources, ce qui est particulièrement mauvais dans un scénario multithreading. Si l'attente occupée est modifiée avec un sommeil, cela a un effet négatif sur la réactivité, même si j'admets que ce n'est probablement pas un gros problème.
Je pense que ma solution résoudra le problème d'origine sans souffrir d'aucun des problèmes ci-dessus:
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.");
}
}
L'appel est bien sûr très simple:
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.");
}
Vous pouvez également utiliser la TryXX(out)
convention, comme le suggère 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;
}
Qui s'appelle comme suit:
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);
Dans les deux cas, vous ne pouvez pas mélanger les appels Reader
avec les Console.ReadLine
appels normaux : si le Reader
délai expire, il y aura un ReadLine
appel suspendu . Au lieu de cela, si vous souhaitez avoir un ReadLine
appel normal (non chronométré) , utilisez simplement le Reader
et omettez le délai d'expiration, de sorte qu'il prenne par défaut un délai d'expiration infini.
Alors, qu'en est-il de ces problèmes des autres solutions que j'ai mentionnées?
- Comme vous pouvez le voir, ReadLine est utilisé, évitant le premier problème.
- La fonction se comporte correctement lorsqu'elle est appelée plusieurs fois. Indépendamment du fait qu'un délai d'expiration se produise ou non, un seul thread d'arrière-plan sera toujours en cours d'exécution et un seul appel à ReadLine au maximum sera actif. L'appel de la fonction entraînera toujours la dernière entrée, ou un délai d'expiration, et l'utilisateur n'aura pas à appuyer sur Entrée plus d'une fois pour soumettre son entrée.
- Et, évidemment, la fonction ne repose pas sur une attente occupée. Au lieu de cela, il utilise des techniques de multithreading appropriées pour éviter de gaspiller des ressources.
Le seul problème que je prévois avec cette solution est qu'elle n'est pas thread-safe. Cependant, plusieurs threads ne peuvent pas vraiment demander à l'utilisateur une entrée en même temps, donc la synchronisation doit avoir lieu avant de passer un appel de Reader.ReadLine
toute façon.