Ich muss ein vorhandenes Programm ändern und es enthält folgenden Code:
var inputs = events.Select(async ev => await ProcessEventAsync(ev))
.Select(t => t.Result)
.Where(i => i != null)
.ToList();
Aber das scheint mir sehr seltsam, vor allem die Verwendung von async
und await
in der Auswahl. Nach dieser Antwort von Stephen Cleary sollte ich diese fallen lassen können.
Dann die zweite, Select
die das Ergebnis auswählt. Bedeutet dies nicht, dass die Aufgabe überhaupt nicht asynchron ist und synchron ausgeführt wird (so viel Aufwand für nichts), oder wird die Aufgabe asynchron ausgeführt und wenn sie erledigt ist, wird der Rest der Abfrage ausgeführt?
Soll ich den obigen Code wie folgt schreiben, gemäß einer anderen Antwort von Stephen Cleary :
var tasks = await Task.WhenAll(events.Select(ev => ProcessEventAsync(ev)));
var inputs = tasks.Where(result => result != null).ToList();
und ist es ganz so?
var inputs = (await Task.WhenAll(events.Select(ev => ProcessEventAsync(ev))))
.Where(result => result != null).ToList();
Während ich an diesem Projekt arbeite, möchte ich das erste Codebeispiel ändern, aber ich bin nicht besonders daran interessiert, asynchronen Code zu ändern (anscheinend zu funktionieren). Vielleicht mache ich mir nur um nichts Sorgen und alle 3 Codebeispiele machen genau das Gleiche?
ProcessEventsAsync sieht folgendermaßen aus:
async Task<InputResult> ProcessEventAsync(InputEvent ev) {...}
quelle
Task<InputResult>
mitInputResult
einer benutzerdefinierten Klasse.Select
die Ergebnisse der Aufgaben vor Ihrem vergessenWhere
.Result
Eigentum der Aufgabe zuzugreifenAntworten:
Der Anruf an
Select
ist gültig. Diese beiden Zeilen sind im Wesentlichen identisch:(Es gibt einen kleinen Unterschied, wie eine synchrone Ausnahme ausgelöst wird
ProcessEventAsync
, aber im Kontext dieses Codes spielt dies überhaupt keine Rolle.)Dies bedeutet, dass die Abfrage blockiert wird. Es ist also nicht wirklich asynchron.
Brechen sie ab:
startet zuerst eine asynchrone Operation für jedes Ereignis. Dann diese Zeile:
wartet darauf, dass diese Vorgänge nacheinander abgeschlossen werden (zuerst wird auf den Vorgang des ersten Ereignisses gewartet, dann auf den nächsten, dann auf den nächsten usw.).
Dies ist der Teil, den ich nicht mag, weil er blockiert und auch Ausnahmen einschließt
AggregateException
.Ja, diese beiden Beispiele sind gleichwertig. Beide starten alle asynchronen Operationen (
events.Select(...)
), warten dann asynchron, bis alle Operationen in beliebiger Reihenfolge abgeschlossen sind (await Task.WhenAll(...)
), und fahren dann mit dem Rest der Arbeit fort (Where...
).Beide Beispiele unterscheiden sich vom ursprünglichen Code. Der ursprüngliche Code blockiert und schließt Ausnahmen ein
AggregateException
.quelle
AggregateException
würde ich im zweiten Code mehrere separate Ausnahmen erhalten?Result
damit wäre eingewickeltAggregateException
.stuff.Select(x => x.Result);
durchawait Task.WhenAll(stuff)
Vorhandener Code funktioniert, blockiert jedoch den Thread.
erstellt für jedes Ereignis eine neue Aufgabe, aber
Blockiert den Thread, der auf das Ende jeder neuen Aufgabe wartet.
Andererseits erzeugt Ihr Code das gleiche Ergebnis, bleibt jedoch asynchron.
Nur ein Kommentar zu Ihrem ersten Code. Diese Linie
erzeugt eine einzelne Aufgabe, daher sollte die Variable im Singular benannt werden.
Schließlich macht Ihr letzter Code dasselbe, ist aber prägnanter
Als Referenz: Task.Wait / Task.WhenAll
quelle
tasks
Variablen betrachten, haben Sie vollkommen recht. Schreckliche Wahl, sie sind nicht einmal Aufgaben, da sie sofort erwartet werden. Ich lasse die Frage einfach so wie sie istMit den aktuellen in Linq verfügbaren Methoden sieht es ziemlich hässlich aus:
Hoffentlich werden die folgenden Versionen von .NET elegantere Tools für die Bearbeitung von Aufgabensammlungen und Aufgaben von Sammlungen bieten.
quelle
Ich habe diesen Code verwendet:
so was:
quelle
Func<TSource, Task<TResult>> method
dasother params
auf dem zweiten Codebit erwähnte enthalten ?Select()
, also ein elegantes Drop-In.Ich bevorzuge dies als Erweiterungsmethode:
Damit es mit Methodenverkettung verwendet werden kann:
quelle
Wait
wenn sie nicht tatsächlich wartet. Es wird eine Aufgabe erstellt, die abgeschlossen ist, wenn alle Aufgaben abgeschlossen sind. Nennen Sie esWhenAll
wie dieTask
Methode, die es emuliert. Es ist auch sinnlos für die Methode zu seinasync
. Rufen Sie einfach anWhenAll
und fertig.WhenAll
eine ausgewertete Liste zurückgegeben wird (sie wird nicht träge ausgewertet), kann argumentiert werden, dass derTask<T[]>
Rückgabetyp verwendet wird, um dies anzuzeigen. Wenn dies erwartet wird, kann Linq weiterhin verwendet werden, es wird jedoch auch mitgeteilt, dass es nicht faul ist.