Beim Warten auf Dispatcher.RunAsync
erfolgt die Fortsetzung, wenn die Arbeit geplant ist, nicht wenn die Arbeit abgeschlossen ist. Wie kann ich auf die Fertigstellung der Arbeit warten?
Bearbeiten
Meine ursprüngliche Frage ging davon aus, dass die vorzeitige Fortsetzung durch das Design der API verursacht wurde. Hier ist die eigentliche Frage.
Beim Warten auf Dispatcher.RunAsync
mit einem asynchronen Delegaten, der await
im Code des Stellvertreters verwendet, tritt die Fortsetzung auf, wenn await
auftritt, nicht wenn die Arbeit abgeschlossen ist. Wie kann ich auf die Fertigstellung der Arbeit warten?
Bearbeiten 2
Ein Grund, warum Sie Arbeiten, die sich bereits auf dem UI-Thread befinden, möglicherweise versenden müssen, ist die Umgehung subtiler Timing- und Layout-Probleme. Es ist durchaus üblich, dass Werte von Größen und Positionen von Elementen im visuellen Baum im Fluss sind und dass die Zeitplanung für eine spätere Iteration der Benutzeroberfläche hilfreich sein kann.
Ich habe den folgenden Vorschlag für ein Microsoft Github-Repository gefunden: How to eine von einem Hintergrund-Thread gesendete UI-Aufgabe .
Definieren Sie diese Erweiterungsmethode für CoreDispatcher
:
Sobald Sie das getan haben, müssen Sie nur die neue Methode RunTaskAsync
verwenden, damit Ihre Hintergrundaufgabe auf der Benutzeroberfläche wartet.
Nehmen wir an, dass dies die Methode ist, die im UI-Thread ausgeführt werden muss. Beachten Sie die Debug-Anweisungen, die Ihnen dabei helfen, den Ablauf zu verfolgen:
%Vor% Um das von Ihrem Hintergrund-Thread abzuwarten, würden Sie RunTaskAsync
verwenden:
Die Ausgabe sieht dann so aus:
Object_Callback () wurde aufgerufen.
Warten auf Benutzerauswahl ...
Der Benutzer hat eine Wahl getroffen. Rückgabeergebnis.
Object_Callback () wird erneut ausgeführt. Benutzer hat auf Schaltfläche 1 geklickt.
Ihre Frage geht davon aus, dass Sie einen Hintergrundthread
einplanen möchten (und darauf warten)Sie werden normalerweise feststellen, dass Ihr Code viel sauberer und einfacher zu verstehen ist (und er wird definitiv portabler sein), wenn Sie die UI als "Master" und die Hintergrundthreads als " Sklaven ".
Anstatt also den Hintergrund-Thread await
für den UI-Thread auszuführen (mit dem ungeschickten und nicht portierbaren Dispatcher.RunAsync
), haben Sie den UI-Thread await
einige Operation für den Hintergrund-Thread (benutze die portable, für asynchrone Task.Run
).
Sie können den Aufruf in Ihrer eigenen asynchronen Methode, die erwartet werden kann, in RunAsync
umbrechen und den Abschluss der Aufgabe und damit die Fortsetzung des Wartens auf Anrufer selbst steuern.
Da async-await auf den Typ Task
zentriert ist, müssen Sie die Arbeit mit diesem Typ orchestrieren. Normalerweise wird jedoch ein Task
so geplant, dass es in einem Threadpool-Thread ausgeführt wird. Daher kann es nicht zum Planen von UI-Arbeiten verwendet werden.
Allerdings wurde der TaskCompletionSource
-Typ erfunden, um als eine Art Puppenspieler für eine nicht geplante Task
zu fungieren. Mit anderen Worten, ein TaskCompletionSource
kann einen Dummy Task
erstellen, der nicht geplant ist, etwas zu tun, aber über Methoden auf TaskCompletionSource
kann scheinbar laufen und sich wie ein normaler Job abschließen.
Siehe dieses Beispiel.
%Vor%Hinweis: Die Hauptarbeit im UI-Thread besteht darin, dass alle 100 ms nur einige Zeilen auf dem Bildschirm gezeichnet werden.
A TaskCompletionSource
wird als Puppenstamm erstellt. Schauen Sie in die Nähe des Endes und Sie werden sehen, dass es eine Eigenschaft Task
hat, die an den Aufrufer zurückgegeben wird. Die Rückgabe von Task
erfüllt die Compiler-Anforderungen und macht die Methode erwartbar und asynchron.
Allerdings ist Task
nur eine Puppe, ein Stellvertreter für die eigentliche Arbeit im UI-Thread.
Siehe, wie in diesem Hauptbenutzeroberflächen-Delegaten die Methode TaskCompletionSource.SetResult
verwendet wird, um ein Ergebnis in Task
zu erzwingen (seit dem Aufruf an den Aufrufer) und mitzuteilen, dass die Arbeit beendet ist.
Wenn es einen Fehler gibt, verwende ich SetException
, um 'eine andere Zeichenkette zu ziehen' und lasse den Eindruck entstehen, dass sich in der Puppe Task
eine Ausnahme gebildet hat.
Das async-await-Subsystem kennt keine Unterschiede und funktioniert so, wie Sie es erwarten würden.
Bearbeiten
Wenn die Methode von svick dazu veranlasst wurde, nur vom UIhread aufrufbar zu sein, würde dies genügen:
%Vor%Eine nette Methode, sauber zu arbeiten @StephenCleary schlägt vor, selbst wenn Sie aus irgendeinem Grund mit einem Worker-Thread beginnen müssen, ein einfaches Hilfsobjekt zu verwenden. Mit dem folgenden Objekt können Sie einen Code wie folgt schreiben:
%Vor%In Ihrem App.OnLaunched müssen Sie das Objekt initialisieren:
%Vor%Die Theorie hinter dem folgenden Code finden Sie unter alles erwarten;
%Vor%Tags und Links c# async-await windows-runtime windows-store-apps