Ich habe eine Liste von Aufgaben, die ich wie folgt erstellt habe:
public async Task<IList<Foo>> GetFoosAndDoSomethingAsync()
{
var foos = await GetFoosAsync();
var tasks = foos.Select(async foo => await DoSomethingAsync(foo)).ToList();
...
}
Durch die Verwendung .ToList()
sollten alle Aufgaben beginnen. Jetzt möchte ich auf ihre Fertigstellung warten und die Ergebnisse zurückgeben.
Dies funktioniert im obigen ...
Block:
var list = new List<Foo>();
foreach (var task in tasks)
list.Add(await task);
return list;
Es macht was ich will, aber das scheint ziemlich ungeschickt. Ich würde viel lieber so etwas Einfacheres schreiben:
return tasks.Select(async task => await task).ToList();
... aber das wird nicht kompiliert. Was vermisse ich? Oder ist es einfach nicht möglich, Dinge so auszudrücken?
c#
linq
async-await
Matt Johnson-Pint
quelle
quelle
DoSomethingAsync(foo)
für jedes Foo seriell verarbeiten , oder ist dies ein Kandidat für Parallel.ForEach <Foo> ?Parallel.ForEach
blockiert. Das Muster hier stammt aus Jon Skeets asynchronem C # -Video auf Pluralsight . Es wird parallel ausgeführt, ohne zu blockieren..ToList()
wenn ich es nur benutzen werdeWhenAll
.)DoSomethingAsync
geschrieben wird, wird die Liste möglicherweise parallel ausgeführt oder nicht. Ich konnte eine Testmethode schreiben, die es war, und eine Version, die es nicht war, aber in beiden Fällen wird das Verhalten von der Methode selbst bestimmt, nicht vom Delegaten, der die Aufgabe erstellt. Entschuldigung für die Verwechslung. Wenn Sie jedochDoSomethingAsyc
zurückkehrenTask<Foo>
, ist dasawait
im Delegierten nicht unbedingt erforderlich ... Ich denke, das war der Hauptpunkt, den ich versuchen wollte.Antworten:
LINQ funktioniert nicht perfekt mit
async
Code, aber Sie können dies tun:Wenn Ihre Aufgaben alle denselben Werttyp zurückgeben, können Sie sogar Folgendes tun:
das ist ganz nett.
WhenAll
Gibt ein Array zurück, daher glaube ich, dass Ihre Methode die Ergebnisse direkt zurückgeben kann:quelle
var tasks = foos.Select(foo => DoSomethingAsync(foo)).ToList();
var tasks = foos.Select(DoSomethingAsync).ToList();
Select
. Aber die meisten mögen es nichtWhere
.async
Threads reduzieren. Wenn es CPU-gebunden ist und sich bereits in einem Hintergrund-Thread befindet,async
bietet es keinen Vorteil.Um Stephens Antwort zu erweitern, habe ich die folgende Erweiterungsmethode erstellt, um den fließenden Stil von LINQ beizubehalten. Sie können dann tun
quelle
ToArrayAsync
Ein Problem mit Task.WhenAll ist, dass dadurch eine Parallelität erzeugt wird. In den meisten Fällen ist es vielleicht sogar noch besser, aber manchmal möchten Sie es vermeiden. Beispiel: Lesen von Daten in Stapeln aus der Datenbank und Senden von Daten an einen Remote-Webdienst. Sie möchten nicht alle Stapel in den Speicher laden, sondern die Datenbank aufrufen, sobald der vorherige Stapel verarbeitet wurde. Sie müssen also die Asynchronität aufheben. Hier ist ein Beispiel:
Hinweis .GetAwaiter (). GetResult () konvertiert es in Synchronose. DB würde nur dann faul getroffen, wenn BatchSize von Ereignissen verarbeitet wurden.
quelle
Verwenden Sie
Task.WaitAll
oderTask.WhenAll
was auch immer angemessen ist.quelle
Task.WaitAll
blockiert, ist nicht zu erwarten und funktioniert nicht mit aTask<T>
.WhenAll
?Task.WhenAll sollte hier den Trick machen.
quelle