Ich bin überrascht zu erfahren, dass nach 5 Jahren alle Antworten immer noch unter einem oder mehreren der folgenden Probleme leiden:
- Eine andere Funktion als ReadLine wird verwendet, was zu einem Funktionsverlust führt. (Löschen / Rücktaste / Aufwärts-Taste für vorherige Eingabe).
- Die Funktion verhält sich schlecht, wenn sie mehrmals aufgerufen wird (Laichen mehrerer Threads, viele hängende ReadLines oder anderweitig unerwartetes Verhalten).
- Die Funktion beruht auf einer Wartezeit. Dies ist eine schreckliche Verschwendung, da die Wartezeit voraussichtlich zwischen einigen Sekunden und dem Zeitlimit liegen kann, das mehrere Minuten betragen kann. Eine Wartezeit, die so lange dauert, ist ein schrecklicher Ressourcenverbrauch, der in einem Multithreading-Szenario besonders schlimm ist. Wenn das Besetzt-Warten mit einem Schlaf geändert wird, wirkt sich dies negativ auf die Reaktionsfähigkeit aus, obwohl ich zugebe, dass dies wahrscheinlich kein großes Problem ist.
Ich glaube, meine Lösung wird das ursprüngliche Problem lösen, ohne an einem der oben genannten Probleme zu leiden:
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.");
}
}
Das Anrufen ist natürlich sehr einfach:
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.");
}
Alternativ können Sie die TryXX(out)
Konvention verwenden, wie von Shmueli vorgeschlagen:
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;
}
Welches heißt wie folgt:
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 beiden Fällen können Sie Anrufe nicht Reader
mit normalen Console.ReadLine
Anrufen mischen : Wenn die Reader
Zeit abgelaufen ist, wird ein ReadLine
Anruf hängen bleiben . Wenn Sie stattdessen einen normalen (nicht zeitgesteuerten) ReadLine
Anruf haben möchten , verwenden Sie einfach das Reader
und lassen Sie das Zeitlimit weg, sodass standardmäßig ein unendliches Zeitlimit verwendet wird.
Wie wäre es also mit den Problemen der anderen Lösungen, die ich erwähnt habe?
- Wie Sie sehen können, wird ReadLine verwendet, um das erste Problem zu vermeiden.
- Die Funktion verhält sich bei mehrmaligem Aufruf ordnungsgemäß. Unabhängig davon, ob eine Zeitüberschreitung auftritt oder nicht, wird immer nur ein Hintergrundthread ausgeführt und höchstens ein Aufruf von ReadLine wird jemals aktiv sein. Das Aufrufen der Funktion führt immer zu der neuesten Eingabe oder zu einer Zeitüberschreitung, und der Benutzer muss nicht mehr als einmal die Eingabetaste drücken, um seine Eingabe zu senden.
- Und natürlich beruht die Funktion nicht auf einer Wartezeit. Stattdessen werden geeignete Multithreading-Techniken verwendet, um die Verschwendung von Ressourcen zu verhindern.
Das einzige Problem, das ich bei dieser Lösung sehe, ist, dass sie nicht threadsicher ist. Mehrere Threads können den Benutzer jedoch nicht gleichzeitig zur Eingabe Reader.ReadLine
auffordern. Daher sollte die Synchronisierung erfolgen, bevor Sie einen Anruf tätigen.
horrible waste
, aber natürlich ist Ihre Signalisierung überlegen. Die Verwendung eines blockierendenConsole.ReadLine
Anrufs in einer Endlosschleife in einer zweiten Bedrohung verhindert außerdem die Probleme mit vielen solchen Anrufen, die im Hintergrund herumhängen, wie in den anderen, stark positiv bewerteten Lösungen unten. Vielen Dank, dass Sie Ihren Code geteilt haben. +1Console.ReadLine()
Aufruf zu brechen . Sie erhalten ein "Phantom"ReadLine
, das zuerst ausgefüllt werden muss.getInput
.quelle
ReadLine
Sie anrufen, sitzt dort und wartet auf Eingaben. Wenn Sie es 100 Mal aufrufen, werden 100 Threads erstellt, die nicht alle verschwinden, bis Sie 100 Mal die Eingabetaste drücken!Hilft dieser Ansatz mit Console.KeyAvailable ?
quelle
KeyAvailable
Zeigt nur an, dass der Benutzer mit der Eingabe von ReadLine-Eingaben begonnen hat. Beim Drücken der Eingabetaste ist jedoch ein Ereignis erforderlich, durch das ReadLine zurückkehrt. Diese Lösung funktioniert nur für ReadKey, dh es wird nur ein Zeichen abgerufen. Da dies die eigentliche Frage für ReadLine nicht löst, kann ich Ihre Lösung nicht verwenden. -1 EntschuldigungDas hat bei mir funktioniert.
quelle
Auf die eine oder andere Weise benötigen Sie einen zweiten Thread. Sie können asynchrone E / A verwenden, um zu vermeiden, dass Sie Ihre eigenen deklarieren:
Wenn der Lesevorgang Daten zurückgibt, legen Sie das Ereignis fest und Ihr Hauptthread wird fortgesetzt, andernfalls fahren Sie nach dem Timeout fort.
quelle
quelle
Ich denke, Sie müssen einen sekundären Thread erstellen und nach einem Schlüssel auf der Konsole suchen. Ich kenne keinen eingebauten Weg, um dies zu erreichen.
quelle
Ich hatte 5 Monate lang mit diesem Problem zu kämpfen, bevor ich eine Lösung fand, die in einem Unternehmen perfekt funktioniert.
Das Problem bei den meisten bisherigen Lösungen besteht darin, dass sie sich auf etwas anderes als Console.ReadLine () stützen, und Console.ReadLine () bietet viele Vorteile:
Meine Lösung lautet wie folgt:
Beispielcode:
Weitere Informationen zu dieser Technik, einschließlich der richtigen Technik zum Abbrechen eines Threads, der Console.ReadLine verwendet:
.NET-Aufruf zum Senden eines Tastendrucks [Enter] in den aktuellen Prozess. Welche Konsolen-App ist das?
Wie kann ich einen anderen Thread in .NET abbrechen, wenn dieser Thread Console.ReadLine ausführt?
quelle
Das Aufrufen von Console.ReadLine () im Delegaten ist fehlerhaft, da dieser Aufruf niemals zurückgegeben wird, wenn der Benutzer nicht die Eingabetaste drückt. Der Thread, der den Delegaten ausführt, wird blockiert, bis der Benutzer die Eingabetaste drückt, ohne dass er abgebrochen werden kann.
Das Ausgeben einer Folge dieser Aufrufe verhält sich nicht wie erwartet. Beachten Sie Folgendes (anhand des Beispiels Konsolenklasse von oben):
Der Benutzer lässt das Zeitlimit für die erste Eingabeaufforderung ablaufen und gibt dann einen Wert für die zweite Eingabeaufforderung ein. Sowohl Vorname als auch Nachname enthalten die Standardwerte. Wenn der Benutzer die Eingabetaste drückt, wird der erste ReadLine-Aufruf abgeschlossen, aber der Code hat diesen Aufruf abgebrochen und das Ergebnis im Wesentlichen verworfen. Der zweite ReadLine-Aufruf wird weiterhin blockiert, das Zeitlimit läuft schließlich ab und der zurückgegebene Wert ist wieder der Standardwert.
Übrigens: Der obige Code enthält einen Fehler. Durch Aufrufen von waitHandle.Close () schließen Sie das Ereignis unter dem Arbeitsthread aus. Wenn der Benutzer nach Ablauf des Zeitlimits die Eingabetaste drückt, versucht der Arbeitsthread, das Ereignis zu signalisieren, das eine ObjectDisposedException auslöst. Die Ausnahme wird vom Arbeitsthread ausgelöst. Wenn Sie keinen nicht behandelten Ausnahmebehandler eingerichtet haben, wird der Prozess beendet.
quelle
Wenn Sie in der
Main()
Methode sind, können Sie nicht verwendenawait
, also müssen Sie verwendenTask.WaitAny()
:C # 7.1 bietet jedoch die Möglichkeit, eine asynchrone
Main()
Methode zu erstellen. Daher ist es besser, dieTask.WhenAny()
Version zu verwenden, wenn Sie diese Option haben:quelle
Ich lese möglicherweise zu viel in die Frage, aber ich gehe davon aus, dass das Warten dem Startmenü ähnelt, in dem es 15 Sekunden wartet, es sei denn, Sie drücken eine Taste. Sie können entweder (1) eine Blockierungsfunktion oder (2) einen Thread, ein Ereignis und einen Timer verwenden. Das Ereignis würde als "Weiter" fungieren und blockieren, bis entweder der Timer abgelaufen ist oder eine Taste gedrückt wurde.
Pseudocode für (1) wäre:
quelle
Ich kann Gulzars Beitrag leider nicht kommentieren, aber hier ist ein ausführlicheres Beispiel:
quelle
BEARBEITEN : Das Problem wurde behoben, indem die eigentliche Arbeit in einem separaten Prozess ausgeführt wurde und dieser Prozess abgebrochen wurde, wenn das Zeitlimit überschritten wurde. Siehe unten für Details. Wütend!
Ich habe es nur ausprobiert und es schien gut zu funktionieren. Mein Kollege hatte eine Version, die ein Thread-Objekt verwendete, aber ich finde die BeginInvoke () -Methode von Delegatentypen etwas eleganter.
Das ReadLine.exe-Projekt ist sehr einfach und hat eine Klasse, die so aussieht:
quelle
Console.ReadLine()
sind und auf die nächste Anforderung wird halten Eingang blockiert. Die akzeptierte Antwort ist ziemlich nah, hat aber immer noch Einschränkungen.ReadLine()
Sie dann einen anderen in Ihrem Programm, nachdem Sie diesen aufgerufen haben. Schau was passiert. Sie müssen ZWEIMAL die Eingabetaste drücken, um es zum Laufen zu bringen, da die Single-Thread-Funktion desConsole
. Es. Tut nicht. Arbeit..NET 4 macht dies mithilfe von Aufgaben unglaublich einfach.
Bauen Sie zuerst Ihren Helfer:
Zweitens mit einer Aufgabe ausführen und warten:
Es gibt keinen Versuch, die ReadLine-Funktionalität neu zu erstellen oder andere gefährliche Hacks durchzuführen, um dies zum Laufen zu bringen. Mit Aufgaben können wir die Frage auf ganz natürliche Weise lösen.
quelle
Als ob es hier nicht schon genug Antworten gäbe: 0), kapselt das Folgende in eine statische Methode @ kwls Lösung oben (die erste).
Verwendung
quelle
Einfaches Threading-Beispiel, um dies zu lösen
oder eine statische Zeichenfolge oben, um eine ganze Zeile zu erhalten.
quelle
Ich bin mein Fall, das funktioniert gut:
quelle
Ist das nicht schön kurz?
quelle
Dies ist ein umfassenderes Beispiel für die Lösung von Glen Slayden. Ich habe dies gerne gemacht, als ich einen Testfall für ein anderes Problem erstellt habe. Es verwendet asynchrone E / A und ein manuelles Rücksetzereignis.
quelle
Mein Code basiert vollständig auf der Antwort des Freundes @JSQuareD
Aber ich musste den
Stopwatch
Timer verwenden, denn als ich das ProgrammConsole.ReadKey()
damit beendet hatte, wartete es immer nochConsole.ReadLine()
und es erzeugte unerwartetes Verhalten.Es hat perfekt für mich funktioniert. Behält die ursprüngliche Console.ReadLine () bei
quelle
Ein anderer billiger Weg, um einen zweiten Thread zu erhalten, besteht darin, ihn in einen Delegaten zu packen.
quelle
Beispielimplementierung von Erics Beitrag oben. In diesem Beispiel wurden Informationen gelesen, die über Pipe an eine Konsolen-App übergeben wurden:
quelle
Beachten Sie, dass Sie einige der coolen Funktionen von ReadLine verlieren, wenn Sie die Route "Console.ReadKey" wählen, nämlich:
Ändern Sie die while-Schleife entsprechend, um eine Zeitüberschreitung hinzuzufügen.
quelle
Bitte hasse mich nicht dafür, dass ich der Fülle der vorhandenen Antworten eine weitere Lösung hinzugefügt habe! Dies funktioniert für Console.ReadKey (), kann jedoch leicht geändert werden, um mit ReadLine () usw. zu arbeiten.
Da die „Console.Read“ Methoden blockiert werden, ist es notwendig, „ Schub des StdIn Stromes“ die Lese abzubrechen.
Aufrufsyntax:
Code:
quelle
Hier ist eine Lösung, die verwendet
Console.KeyAvailable
. Diese blockieren Anrufe, aber es sollte ziemlich trivial sein, sie auf Wunsch asynchron über die TPL aufzurufen. Ich habe die Standard-Stornierungsmechanismen verwendet, um die Verbindung mit dem Task Asynchronous Pattern und all den guten Dingen zu vereinfachen.Dies hat einige Nachteile.
ReadLine
(Bildlauf nach oben / unten usw.).quelle
Bin hier gelandet, weil eine doppelte Frage gestellt wurde. Ich habe die folgende Lösung gefunden, die einfach aussieht. Ich bin sicher, es hat einige Nachteile, die ich verpasst habe.
quelle
Ich bin zu dieser Antwort gekommen und habe am Ende Folgendes getan:
quelle
Ein einfaches Beispiel mit
Console.KeyAvailable
:quelle
Viel zeitgemäßerer und aufgabenbasierter Code würde ungefähr so aussehen:
quelle
Ich hatte eine einzigartige Situation mit einer Windows-Anwendung (Windows-Dienst). Bei der interaktiven Ausführung des Programms
Environment.IsInteractive
(VS Debugger oder von cmd.exe) habe ich AttachConsole / AllocConsole verwendet, um mein stdin / stdout abzurufen. Um zu verhindern, dass der Prozess während der Arbeit beendet wird, ruft der UI-Thread aufConsole.ReadKey(false)
. Ich wollte das Warten auf den UI-Thread von einem anderen Thread abbrechen, daher habe ich eine Änderung der Lösung von @JSquaredD gefunden.quelle