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.
horrible waste
, mais bien sûr, votre signalisation est supérieure. De plus, l'utilisation d'unConsole.ReadLine
appel de blocage dans une boucle infinie dans une deuxième menace évite les problèmes liés à de nombreux appels de ce type qui traînent en arrière-plan comme dans les autres solutions fortement votées ci-dessous. Merci d'avoir partagé votre code. +1Console.ReadLine()
appel suivant que vous effectuez. Vous vous retrouvez avec un «fantôme»ReadLine
qui doit d'abord être complété.getInput
.la source
ReadLine
vous appelez reste là en attente d'une entrée. Si vous l'appelez 100 fois, cela crée 100 threads qui ne disparaissent pas jusqu'à ce que vous appuyiez 100 fois sur Entrée!Cette approche utilisant Console.KeyAvailable aidera-t-elle?
la source
KeyAvailable
indique seulement que l'utilisateur a commencé à taper une entrée dans ReadLine, mais nous avons besoin d'un événement en appuyant sur Entrée, qui oblige ReadLine à revenir. Cette solution ne fonctionne que pour ReadKey, c'est-à-dire obtenir un seul caractère. Comme cela ne résout pas la question réelle de ReadLine, je ne peux pas utiliser votre solution. -1 désoléCela a fonctionné pour moi.
la source
D'une manière ou d'une autre, vous avez besoin d'un deuxième fil. Vous pouvez utiliser des E / S asynchrones pour éviter de déclarer les vôtres:
Si la lecture renvoie des données, définissez l'événement et votre thread principal continuera, sinon vous continuerez après l'expiration du délai.
la source
la source
Je pense que vous devrez créer un fil secondaire et rechercher une clé sur la console. Je ne connais aucun moyen intégré pour y parvenir.
la source
J'ai lutté avec ce problème pendant 5 mois avant de trouver une solution qui fonctionne parfaitement dans un environnement d'entreprise.
Le problème avec la plupart des solutions jusqu'à présent est qu'elles reposent sur autre chose que Console.ReadLine () et Console.ReadLine () présente de nombreux avantages:
Ma solution est la suivante:
Exemple de code:
Plus d'informations sur cette technique, y compris la technique correcte pour abandonner un thread qui utilise Console.ReadLine:
Appel .NET pour envoyer une touche [entrée] dans le processus en cours, qui est une application console?
Comment abandonner un autre thread dans .NET, lorsque ledit thread exécute Console.ReadLine?
la source
L'appel de Console.ReadLine () dans le délégué est incorrect car si l'utilisateur n'appuie pas sur «entrée», cet appel ne reviendra jamais. Le thread exécutant le délégué sera bloqué jusqu'à ce que l'utilisateur clique sur «entrée», sans moyen de l'annuler.
L'émission d'une séquence de ces appels ne se comportera pas comme prévu. Considérez ce qui suit (en utilisant l'exemple de classe Console ci-dessus):
L'utilisateur laisse le délai d'expiration pour la première invite, puis entre une valeur pour la deuxième invite. FirstName et lastName contiendront les valeurs par défaut. Lorsque l'utilisateur clique sur «enter», le premier appel ReadLine se termine, mais le code a abandonné cet appel et a essentiellement rejeté le résultat. Le deuxième appel ReadLine continuera à se bloquer, le délai d'expiration finira par expirer et la valeur renvoyée sera à nouveau la valeur par défaut.
BTW- Il y a un bogue dans le code ci-dessus. En appelant waitHandle.Close (), vous fermez l'événement sous le thread de travail. Si l'utilisateur frappe «entrée» après l'expiration du délai, le thread de travail tentera de signaler l'événement qui lève une ObjectDisposedException. L'exception est levée à partir du thread de travail, et si vous n'avez pas configuré de gestionnaire d'exceptions non géré, votre processus se terminera.
la source
Si vous êtes dans la
Main()
méthode, vous ne pouvez pas utiliserawait
, vous devrez donc utiliserTask.WaitAny()
:Cependant, C # 7.1 introduit la possibilité de créer une
Main()
méthode asynchrone , il est donc préférable d'utiliser laTask.WhenAny()
version chaque fois que vous avez cette option:la source
Je lis peut-être trop dans la question, mais je suppose que l'attente serait similaire au menu de démarrage où elle attend 15 secondes à moins que vous n'appuyiez sur une touche. Vous pouvez utiliser (1) une fonction de blocage ou (2) vous pouvez utiliser un thread, un événement et une minuterie. L'événement agirait comme un «continuer» et se bloquerait jusqu'à ce que le minuteur expire ou qu'une touche soit enfoncée.
Le pseudo-code pour (1) serait:
la source
Je ne peux malheureusement pas commenter le post de Gulzar, mais voici un exemple plus complet:
la source
EDIT : résolu le problème en effectuant le travail réel dans un processus séparé et en tuant ce processus s'il expire. Voir ci-dessous pour plus de détails. Ouf!
Je viens de faire un tour et cela a semblé bien fonctionner. Mon collègue avait une version qui utilisait un objet Thread, mais je trouve que la méthode BeginInvoke () des types de délégués est un peu plus élégante.
Le projet ReadLine.exe est très simple qui a une classe qui ressemble à ceci:
la source
Console.ReadLine()
sont bloquantes et bloquent l'entrée à la demande suivante. La réponse acceptée est assez proche, mais a encore des limites.ReadLine()
dans votre programme après avoir appelé celui-ci. Voyez ce qui se passe. Vous devez appuyer deux fois sur return pour le faire fonctionner en raison de la nature à thread unique duConsole
. Il. Non. Travail..NET 4 rend cela incroyablement simple à l'aide de tâches.
Tout d'abord, construisez votre assistant:
Deuxièmement, exécutez une tâche et attendez:
Il est impossible d'essayer de recréer la fonctionnalité ReadLine ou d'effectuer d'autres hacks périlleux pour que cela fonctionne. Les tâches nous permettent de résoudre la question de manière très naturelle.
la source
Comme s'il n'y avait pas déjà assez de réponses ici: 0), ce qui suit s'encapsule dans une méthode statique @ la solution de kwl ci-dessus (la première).
Usage
la source
Exemple de filetage simple pour résoudre ce problème
ou une chaîne statique en haut pour obtenir une ligne entière.
la source
Im mon cas, cela fonctionne bien:
la source
N'est-ce pas joli et court?
la source
Ceci est un exemple plus complet de la solution de Glen Slayden. J'ai eu l'occasion de le faire lors de la création d'un scénario de test pour un autre problème. Il utilise des E / S asynchrones et un événement de réinitialisation manuelle.
la source
Mon code est entièrement basé sur la réponse de l'ami @JSQuareD
Mais j'avais besoin d'utiliser la
Stopwatch
minuterie parce que lorsque j'ai terminé le programme avec,Console.ReadKey()
il attendait toujoursConsole.ReadLine()
et cela a généré un comportement inattendu.Cela a fonctionné parfaitement pour moi. Conserve la console d'origine.ReadLine ()
la source
Un autre moyen peu coûteux d'obtenir un deuxième fil est de l'envelopper dans un délégué.
la source
Exemple d'implémentation du post d'Eric ci-dessus. Cet exemple particulier a été utilisé pour lire les informations transmises à une application console via un tube:
la source
Notez que si vous suivez la route «Console.ReadKey», vous perdez certaines des fonctionnalités intéressantes de ReadLine, à savoir:
Pour ajouter un délai, modifiez la boucle while en conséquence.
la source
Ne me détestez pas pour avoir ajouté une autre solution à la pléthore de réponses existantes! Cela fonctionne pour Console.ReadKey (), mais pourrait facilement être modifié pour fonctionner avec ReadLine (), etc.
Comme les méthodes «Console.Read» bloquent, il est nécessaire de « pousser » le flux StdIn pour annuler la lecture.
Syntaxe d'appel:
Code:
la source
Voici une solution qui utilise
Console.KeyAvailable
. Ce sont des appels bloquants, mais il devrait être assez simple de les appeler de manière asynchrone via le TPL si vous le souhaitez. J'ai utilisé les mécanismes d'annulation standard pour faciliter le câblage avec le modèle asynchrone de tâches et toutes ces bonnes choses.Il y a quelques inconvénients à cela.
ReadLine
fournies (défilement des flèches haut / bas, etc.).la source
Terminé ici parce qu'une question en double a été posée. J'ai proposé la solution suivante qui semble simple. Je suis sûr qu'il a quelques inconvénients que j'ai manqués.
la source
Je suis venu à cette réponse et j'ai fini par faire:
la source
Un exemple simple utilisant
Console.KeyAvailable
:la source
Un code beaucoup plus contemporain et basé sur les tâches ressemblerait à ceci:
la source
J'ai eu une situation unique d'avoir une application Windows (service Windows). Lors de l'exécution du programme de manière interactive
Environment.IsInteractive
(VS Debugger ou à partir de cmd.exe), j'ai utilisé AttachConsole / AllocConsole pour obtenir mon stdin / stdout. Pour empêcher le processus de se terminer pendant le travail, le thread d'interface utilisateur appelleConsole.ReadKey(false)
. Je voulais annuler l'attente que le thread de l'interface utilisateur faisait à partir d'un autre thread, j'ai donc proposé une modification de la solution par @JSquaredD.la source