Ich habe gerade VS2012 bekommen und versucht, es in den Griff zu bekommen async
.
Angenommen, ich habe eine Methode, die einen Wert von einer blockierenden Quelle abruft. Ich möchte nicht, dass der Aufrufer der Methode blockiert. Ich könnte die Methode schreiben, um einen Rückruf anzunehmen, der beim Eintreffen des Werts aufgerufen wird. Da ich jedoch C # 5 verwende, entscheide ich mich, die Methode asynchron zu machen, damit Anrufer sich nicht mit Rückrufen befassen müssen:
// contrived example (edited in response to Servy's comment)
public static Task<string> PromptForStringAsync(string prompt)
{
return Task.Factory.StartNew(() => {
Console.Write(prompt);
return Console.ReadLine();
});
}
Hier ist eine Beispielmethode, die es aufruft. Wenn dies PromptForStringAsync
nicht asynchron wäre, müsste für diese Methode ein Rückruf in einen Rückruf verschachtelt werden. Mit Async kann ich meine Methode auf ganz natürliche Weise schreiben:
public static async Task GetNameAsync()
{
string firstname = await PromptForStringAsync("Enter your first name: ");
Console.WriteLine("Welcome {0}.", firstname);
string lastname = await PromptForStringAsync("Enter your last name: ");
Console.WriteLine("Name saved as '{0} {1}'.", firstname, lastname);
}
So weit, ist es gut. Das Problem ist , wenn ich anrufen GetNameAsync:
public static void DoStuff()
{
GetNameAsync();
MainWorkOfApplicationIDontWantBlocked();
}
Der springende Punkt GetNameAsync
ist, dass es asynchron ist. Ich möchte nicht, dass es blockiert wird, da ich so schnell wie möglich zu MainWorkOfApplicationIDontWantBlocked zurückkehren und GetNameAsync im Hintergrund ausführen lassen möchte. Wenn ich es jedoch so nenne, erhalte ich eine Compiler-Warnung in der GetNameAsync
Zeile:
Warning 1 Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
Mir ist völlig bewusst, dass "die Ausführung der aktuellen Methode fortgesetzt wird, bevor der Aufruf abgeschlossen ist". Das ist der Punkt des asynchronen Codes, richtig?
Ich bevorzuge es, meinen Code ohne Warnungen zu kompilieren, aber hier gibt es nichts zu "reparieren", da der Code genau das tut, was ich beabsichtige. Ich kann die Warnung entfernen, indem ich den Rückgabewert von GetNameAsync
:
public static void DoStuff()
{
var result = GetNameAsync(); // supress warning
MainWorkOfApplicationIDontWantBlocked();
}
Aber jetzt habe ich überflüssigen Code. Visual Studio scheint zu verstehen, dass ich gezwungen war, diesen unnötigen Code zu schreiben, da er die normale Warnung "Wert nie verwendet" unterdrückt.
Ich kann die Warnung auch entfernen, indem ich GetNameAsync in eine Methode einbinde, die nicht asynchron ist:
public static Task GetNameWrapper()
{
return GetNameAsync();
}
Aber das ist noch mehr überflüssig Code. Ich muss also Code schreiben, den ich nicht brauche oder eine unnötige Warnung toleriere.
Gibt es etwas an meiner Verwendung von Async, das hier nicht stimmt?
quelle
PromptForStringAsync
erledigen Sie mehr Arbeit als nötig. Geben Sie einfach das Ergebnis von zurückTask.Factory.StartNew
. Es ist bereits eine Aufgabe, deren Wert die in die Konsole eingegebene Zeichenfolge ist. Es besteht keine Notwendigkeit, darauf zu warten und das Ergebnis zurückzugeben. Dies bringt keinen neuen Wert.GetNameAsync
den vollständigen Namen zur Verfügung zu stellen, die vom Benutzer (dh zur Verfügung gestellt wurdeTask<Name>
, und nicht nur eine RückkehrTask
?DoStuff
Könnte dann speichern , diese Aufgabe, und entwederawait
es nach dem anderen Verfahren oder sogar passiert die Aufgabe zu , dass andere Methode, damit es könnteawait
oderWait
es irgendwo innerhalb seiner Implementierung.async
Schlüsselwort.Antworten:
Wenn Sie das Ergebnis wirklich nicht benötigen, können Sie einfach die
GetNameAsync
Signatur des Benutzers ändern , um Folgendes zurückzugebenvoid
:Überlegen Sie, ob Sie eine Antwort auf eine verwandte Frage erhalten möchten: Was ist der Unterschied zwischen der Rückgabe von void und der Rückgabe einer Aufgabe?
Aktualisieren
Wenn Sie das Ergebnis benötigen, können Sie das ändern
GetNameAsync
, um beispielsweise Folgendes zurückzugebenTask<string>
:Und benutze es wie folgt:
quelle
GetNameAsync
geben keinen Wert zurück (außer natürlich das Ergebnis selbst).void
, kann er nicht wissen, wann es fertig ist. Das habe ich gemeint, als ich in meinem vorherigen Kommentar "das Ergebnis" sagte.async void
Methode haben, außer für Ereignishandler.async void
einer Ausnahme wechseln, die Sie nicht abfangen, was Ihren Prozess zum Absturz bringt, aber in .net 4.5 läuft es weiter.Ich bin ziemlich spät bei dieser Diskussion, aber es gibt auch die Möglichkeit, die
#pragma
Vorprozessor-Direktive zu verwenden. Ich habe hier und da einen asynchronen Code, auf den ich unter bestimmten Bedingungen ausdrücklich nicht warten möchte, und ich mag Warnungen und nicht verwendete Variablen nicht wie der Rest von Ihnen:Das
"4014"
kommt von dieser MSDN-Seite: Compiler-Warnung (Stufe 1) CS4014 .Siehe auch die Warnung / Antwort von @ ryan-horath hier https://stackoverflow.com/a/12145047/928483 .
Update für C # 7.0
C # 7.0 fügt eine neue Funktion hinzu, Variablen verwerfen: Discards - C # Guide , die auch in dieser Hinsicht hilfreich sein kann.
quelle
var
, schreiben Sie einfach_ = SomeMethodAsync();
Ich mag die Lösungen nicht besonders, die entweder die Aufgabe einer nicht verwendeten Variablen zuweisen oder die Methodensignatur so ändern, dass sie void zurückgibt. Ersteres erstellt überflüssigen, nicht intuitiven Code, während letzteres möglicherweise nicht möglich ist, wenn Sie eine Schnittstelle implementieren oder die Funktion, in der Sie die zurückgegebene Aufgabe verwenden möchten, anderweitig verwenden.
Meine Lösung besteht darin, eine Erweiterungsmethode von Task namens DoNotAwait () zu erstellen, die nichts bewirkt. Dies unterdrückt nicht nur alle Warnungen, ReSharper oder andere, sondern macht den Code verständlicher und zeigt zukünftigen Verwaltern Ihres Codes an, dass Sie wirklich beabsichtigt haben, dass der Anruf nicht erwartet wird.
Verlängerungsmethode:
Verwendung:
Bearbeitet, um hinzuzufügen: Dies ähnelt der Lösung von Jonathan Allen, bei der die Erweiterungsmethode die Aufgabe starten würde, wenn sie nicht bereits gestartet wurde. Ich bevorzuge jedoch Einzweckfunktionen, damit die Absicht des Anrufers völlig klar ist.
quelle
async void
IST SCHLECHT!Was ich vorschlage ist, dass Sie das explizit
Task
über eine anonyme Methode ausführen ...z.B
Oder wenn Sie wollten, dass es blockiert, können Sie auf die anonyme Methode warten
Wenn Ihre
GetNameAsync
Methode jedoch mit der Benutzeroberfläche oder einer anderen Benutzeroberfläche interagieren muss (WINRT / MVVM, ich sehe Sie an), wird sie etwas funkiger =)Sie müssen den Verweis wie folgt an den UI-Dispatcher übergeben ...
Und dann müssen Sie in Ihrer asynchronen Methode mit Ihrer Benutzeroberfläche oder an Benutzeroberflächen gebundenen Elementen interagieren, die der Dispatcher für ...
quelle
This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
Dadurch wird auch ein neuer Thread erstellt, während ein neuer Thread nicht unbedingt nur mit async / await erstellt wird.Das mache ich gerade:
Wo
RunConcurrently
ist definiert als ...https://github.com/docevaad/Anchor/blob/master/Tortuga.Anchor/Tortuga.Anchor.source/shared/TaskUtilities.cs
https://www.nuget.org/packages/Tortuga.Anchor/
quelle
public static void Forget(this Task task) { }
async Task
. Einige Aufgaben müssen manuell gestartet werden.Gemäß dem Microsoft-Artikel zu dieser Warnung können Sie sie lösen, indem Sie die zurückgegebene Aufgabe einfach einer Variablen zuweisen. Unten finden Sie eine Übersetzung des im Microsoft-Beispiel bereitgestellten Codes:
Beachten Sie, dass dies in ReSharper zur Meldung "Lokale Variable wird nie verwendet" führt.
quelle
Task
-rückgabefunktionen solltenawait
-ed sein, es sei denn, Sie haben einen sehr guten Grund, dies nicht zu tun . Es gibt hier keinen Grund, warum das Verwerfen der Aufgabe besser wäre als die bereits akzeptierte Antwort auf die Verwendung einerasync void
Methode.async void
führt zu schwerwiegenden Problemen bei der Fehlerbehandlung und führt zu nicht testbarem Code (siehe meinen MSDN-Artikel ). Es wäre weitaus besser, die Variable zu verwenden - wenn Sie absolut sicher sind , dass Ausnahmen stillschweigend verschluckt werden sollen. Wahrscheinlicher wäre es, dass die Operation zweiTask
Sekunden beginnen und dann eine machen möchteawait Task.WhenAll
.async void DoNotWait(Task t) { await t; }
Hilfsmethode kann verwendet werden, um die Nachteile der vonasync void
Ihnen beschriebenen Methoden zu vermeiden . (Und ich denke nicht,Task.WhenAll
was das OP will, aber es könnte sehr gut sein.)Hier eine einfache Lösung.
Grüße
quelle
Es ist Ihr vereinfachtes Beispiel, das den überflüssigen Code verursacht. Normalerweise möchten Sie die Daten verwenden, die zu einem bestimmten Zeitpunkt im Programm von der blockierenden Quelle abgerufen wurden. Sie möchten also das Ergebnis zurück, damit Sie auf die Daten zugreifen können.
Wenn Sie wirklich etwas haben, das völlig isoliert vom Rest des Programms passiert, wäre Async nicht der richtige Ansatz. Starten Sie einfach einen neuen Thread für diese Aufgabe.
quelle
async
( zum Beispiel ) aufgeräumt werden sollMethodWithCallback((result1) => { Use(result1); MethodWithCallback((result2) => { Use(result1,result2); })
Selbst in diesem trivialen Beispiel ist es eine Hündin zu analysieren. Mit Async wird beim Schreibenresult1 = await AsyncMethod(); Use(result1); result2 = await AsyncMethod(); Use(result1,result2);
ein äquivalenter Code für mich generiert, der viel einfacher zu lesen ist (obwohl beide in diesem Kommentar nicht sehr gut lesbar sind!)Use
. Es gibt keinen Grund, auf den synchronen Aufruf von zu warten .Möchten Sie das Ergebnis wirklich ignorieren? wie beim Einschließen unerwarteter Ausnahmen?
Wenn nicht, möchten Sie vielleicht einen Blick auf diese Frage werfen: Fire and Forget-Ansatz ,
quelle
Wenn Sie die Methodensignatur nicht ändern möchten, um sie zurückzugeben
void
(da die Rückgabevoid
immer ungültig sein sollte ), können Sie die C # 7.0+ Discard-Funktion wie diese verwenden, die etwas besser ist als die Zuweisung zu einer Variablen (und die meisten anderen entfernen sollte) Warnungen zu Quellenvalidierungswerkzeugen):quelle