Es gibt sicherlich mehrere Möglichkeiten, dies zu lösen, und einer davon gehört Ihnen. Es ist jedoch nicht wirklich eine gute Übung, lange laufende Aufgaben zu starten, besonders wenn sie nichts tun als synchrones Warten (das ist Thread.Sleep).
Sie sollten Ihren Code in einem technischen und einem Domain-Teil umgestalten. Der technische Teil ist:
- Warten Sie, bis alle Aufgaben in einer bestimmten Sammlung
- abgeschlossen Wenn das dauert nicht mehr regelmäßig über die Fortschritte
Der folgende Code dieses ein bisschen besser zu verstehen, könnte helfen, berichtet. Es startet vier Aufgaben, die verschiedene asynchrone Vorgänge simulieren und warten, bis alle ausgeführt sind. Wenn dies länger als 250 ms dauert, ruft der Aufruf von WhenAllEx weiterhin ein Lambda zum Wiederholen des Fortschrittsberichts auf.
static void Main(string[] args)
{
var tasks = Enumerable.Range(0, 4).Select(taskNumber => Task.Run(async() =>
{
Console.WriteLine("Task {0} starting", taskNumber);
await Task.Delay((taskNumber + 1) * 1000);
Console.WriteLine("Task {0} stopping", taskNumber);
})).ToList();
// Wait for all tasks to complete and do progress report
var whenAll = WhenAllEx(
tasks,
_ => Console.WriteLine("Still in progress. ({0}/{1} completed)", _.Count(task => task.IsCompleted), tasks.Count()));
// Usually never wait for asynchronous operations unless your in Main
whenAll.Wait();
Console.WriteLine("All tasks finished");
Console.ReadKey();
}
/// <summary>
/// Takes a collection of tasks and completes the returned task when all tasks have completed. If completion
/// takes a while a progress lambda is called where all tasks can be observed for their status.
/// </summary>
/// <param name="tasks"></param>
/// <param name="reportProgressAction"></param>
/// <returns></returns>
public static async Task WhenAllEx(ICollection<Task> tasks, Action<ICollection<Task>> reportProgressAction)
{
// get Task which completes when all 'tasks' have completed
var whenAllTask = Task.WhenAll(tasks);
for (; ;)
{
// get Task which completes after 250ms
var timer = Task.Delay(250); // you might want to make this configurable
// Wait until either all tasks have completed OR 250ms passed
await Task.WhenAny(whenAllTask, timer);
// if all tasks have completed, complete the returned task
if (whenAllTask.IsCompleted)
{
return;
}
// Otherwise call progress report lambda and do another round
reportProgressAction(tasks);
}
}
Wird diese Zeile "var whenAllTask = Task.WhenAll (tasks);" blockiert werden, bis alle Aufgaben abgeschlossen sind? und der Code wird immer auf alle Aufgaben warten und danach Task.Delay ausführen? –
Task.WhenAll (...) blockiert nicht, gibt aber eine Aufgabe zurück, die nach Abschluss aller Aufgaben in den Parametern abgeschlossen ist. Das gleiche gilt für Task.Delay. Es gibt eine Aufgabe zurück, die nach 250ms (im Beispielcode) abgeschlossen ist. Der Trick besteht darin, auf eine dritte Aufgabe zu warten, die abgeschlossen wird, wenn entweder Task.WhenAll OR Task.Delay abgeschlossen ist. Ich werde dem Code einige Kommentare hinzufügen. –
Vielen Dank für Ihre ausführliche Antwort! Ich habe zwei Syntaxfragen. Was geschieht Syntax-weise bei dem Unterstrich, den Sie in den Parametern für 'var whenAll' verwendet haben, und was passiert in der Syntax von' for (;;) '? – cloudcrypt