RunAsync - Wie warte ich auf den Abschluss der Arbeit am UI-Thread?

8

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.

    
Luke Puplett 02.10.2013, 09:18
quelle

4 Antworten

9

Ich habe den folgenden Vorschlag für ein Microsoft Github-Repository gefunden: How to eine von einem Hintergrund-Thread gesendete UI-Aufgabe .

Einrichtung

Definieren Sie diese Erweiterungsmethode für CoreDispatcher :

%Vor%

Sobald Sie das getan haben, müssen Sie nur die neue Methode RunTaskAsync verwenden, damit Ihre Hintergrundaufgabe auf der Benutzeroberfläche wartet.

Anwendungsbeispiel

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:

%Vor%

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.

    
Mike 01.07.2016, 00:08
quelle
8

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 ).

    
Stephen Cleary 02.10.2013 13:38
quelle
5

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%     
Luke Puplett 02.10.2013 09:18
quelle
0

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%     
Marcel W 14.03.2014 09:23
quelle