Laden...

Task, die mit async/await arbeitet, kehrt vorzeitig zurück

Erstellt von Taladan vor 11 Jahren Letzter Beitrag vor 11 Jahren 1.118 Views
Taladan Themenstarter:in
582 Beiträge seit 2008
vor 11 Jahren
Task, die mit async/await arbeitet, kehrt vorzeitig zurück

Hallo,

ich habe einige Objekte die initialisiert werden müssen. Da dies aufgrund von Festplattenzugriffen sehr lange dauert kann, will und muss (laut Metrovorgaben) diese Funktionen paralelisieren.

Im Prinzip gib es ein Basisobjekt, welches durch eine Liste läuft und dann jedes Objekt initialisiert


Task[] tasks = new Task[Tables.Count];

            int i = 0;
            foreach (XDmsTableBase t in Tables)
            {
                tasks[i++] = Task.Factory.StartNew(() => t.CreateSchemaAsync(this));
            }
            Task.WaitAll(tasks);

Mein Problem ist jetzt, dass ich durch Metro zwangsweise ein await einbauen muss und dadurch scheinbar das Task.WaitAll nicht mehr korrekt funktioniert, denn die Programmierung macht weiter, obwohl der Code nach dem Await noch nicht korrekt durchlaufen ist.

override async internal void CreateSchemaAsync(XDms xDms)
        {
...
foreach (s in liste)
   f = await f.CreateFolderAsync(s, CreationCollisionOption.OpenIfExists);
...
}

Jemand eine Idee wie ich das verhindern kann?

Gruß dat Tala

49.485 Beiträge seit 2005
vor 11 Jahren

Hallo Taladan,

await hält (im Gegensatz zu z.B. AutoResetEvent.WaitOne) die Ausführung nicht an der Stelle an, sondern beendet die Ausführung der Methode an dieser Stelle sofort und setzt die Ausführung erst zu einem späteren Zeitpunkt durch einen erneuten Aufruf an dieser Stelle fort.

Mit anderen Worten, eine Methode, die ein await enthält, wird vom Compiler hinter den Kulissen in zwei oder mehre Teilmethoden aufgeteilt, wobei durch den Aufruf der Methode in Wirklichkeit nur der erste Teil aufgerufen wird. Alle folgenden Teile werden dann hinter den Kulissen auf andere Weise aufgerufen.

Dass deine Tasks vorzeitig enden, ist also klar. Works as designed.

Dass du in diese Falle getappt bist, bestätigt meine Skepsis gegenüber async/await. Als ich mir nach deren Einführung angeschaut hatte, wie diese realisiert wurden (mit viel Compiler-Magic), habe ich sofort gedacht, dass viele Programmierer da in die Falle tappen werden. Synchronisation ist kein leichtes Geschäft. Daran ändern leider auch async/await nichts. Die Komplexität der Synchronisation wird dem Programmierer durch async/await leider nicht abgenommen, sondern sie bleibt voll erhalten, sie wird nur vor dem Programmierer versteckt. Das macht es im Grunde noch schlimmer, weil async/await so klar und einfach aussieht, ohne es zu sein. Es lädt geradezu dazu ein, in Unkenntnis der wahren (und versteckten) Abläufe, von falschen Annahmen auszugehen. Im dem Ergebnis, dass der Code, den man schreibt, dann ebenso falsch ist.

herbivore

1.346 Beiträge seit 2008
vor 11 Jahren

Du solltest die CreateSchemaAsync methode so abändern, dass sie einen Task zurüpckgibt(Also auch die Basisklasse ändern, oder aber eine neue Methode schreiben anstatt zu überschreiben). Auch so macht es keinen Sinn CreateSchemaAsync nochmal in einem anderen Thread aufzurufen. Die Task.WaitAll Methode hat den großen Nachteil, das sie den aktuellen Thread anhällt, und damit die Ausführung stoppt. Besser wäre es, wenn du stattdessen ein WhenAll verwendest, und dort dann wieder ein await nutzt. So hast du keinen wartenden Thread mehr.
Demnach würde ich die Initialisierung so durchführen:


await Task.WhenAll(Tables.Select(a => a.CreateSchemaAsync(this)).ToArray());

Und CreateSchemaAsync anstatt void Tast zurückgeben lassen, oder eben eine neue Methode erstellen, die einen Task zurückgibt.

Taladan Themenstarter:in
582 Beiträge seit 2008
vor 11 Jahren

Hi,

Danke für die Hilfe, bin aber gerade auf eine andere Lösung gestoßen. Ich habe es jetzt als void gelöst und die kritische Stelle in

Statt


f = await f.CreateFolderAsync(s, CreationCollisionOption.OpenIfExists)

in


f = f.CreateFolderAsync(s, CreationCollisionOption.OpenIfExists).AsTask().Result

geändert. Jetzt funktioniert es auch.

Gruß dat Tala