Warten Sie, bis die Aufgabe abgeschlossen ist, ohne den UI-Thread zu blockieren

8

Ich habe eine recht komplexe WPF-Anwendung, die (ähnlich wie VS2013) IDocuments und ITools in der Haupt-Shell der Anwendung angedockt hat. Einer dieser Tools muss sicher heruntergefahren werden, wenn das Hauptfenster geschlossen ist, um nicht in einen "schlechten" Zustand zu geraten. Also verwende ich die public override void CanClose(Action<bool> callback) -Methode von Caliburn Micro, um einige Datenbankupdates usw. durchzuführen. Das Problem, das ich habe, ist der gesamte Update-Code in dieser Methode, der MongoDB Driver 2.0 verwendet und dieses Zeug ist async . Irgendein Code; zur Zeit versuche ich

auszuführen %Vor%

Nun, um mit zu beginnen, hatte ich nicht ManualResetEventSlim und dies würde offensichtlich zum CanClose -Aufrufer zurückkehren, bevor ich meine Datenbank im Hintergrund [thread-pool] -Thread aktualisiert habe. In einem Versuch, die Rückkehr zu verhindern, bis ich meine Updates fertig habe, habe ich versucht, die Rückkehr zu blockieren, aber das friert den UI-Thread ein und verhindert, dass etwas passiert.

Wie kann ich meinen Bereinigungscode ausführen, ohne zu früh zum Anrufer zurückzukehren?

Danke für Ihre Zeit.

Beachten Sie, dass ich die Methode OnClose nicht mit einer asynchronen Signatur überschreiben kann, da der aufrufende Code nicht darauf warten würde (ich habe keine Kontrolle darüber).

    
MoonKnight 23.08.2015, 14:07
quelle

3 Antworten

7

Ich glaube nicht, dass Sie viel Wahl haben, als die Rückkehr zu blockieren. Ihre Aktualisierungen sollten dennoch ausgeführt werden, obwohl der Benutzeroberflächen-Thread gesperrt ist. Ich würde kein ManualResetEventSlim verwenden, sondern nur eine einfache wait () und eine einzelne Aufgabe ohne eine Fortsetzung. Der Grund dafür ist standardmäßig Task.Run verhindert, dass der untergeordnete Task (Ihre Fortsetzung) an den übergeordneten Thread angehängt wird und Ihre Fortsetzung möglicherweise keine Zeit hat, bevor das Fenster geschlossen wird, siehe diesen Beitrag .

%Vor%

Sie können auch TaskFactory.StartNew mit TaskCreationOptions.AttachedToParent verwenden, wenn Sie wirklich eine Fortsetzung benötigen.

BEARBEITEN: Ich habe meine Antwort mit @Saeb Amini kombiniert, es ist insgesamt ein bisschen ein Hack, aber Sie behalten eine gewisse Reaktion auf die Benutzeroberfläche.

EDIT 2: Hier ist eine Beispieldemonstration der Lösung (getestet mit einem neuen WPF-Projekt):

%Vor%     
Julien Lebot 23.08.2015, 14:53
quelle
4

Sie können etwas ähnliches zu WinForms Application.DoEvents Für WPF wird jedoch ein Flag verwendet, das Ihre Aufgabe auslöst, nicht Wait ing dafür, aber die UI-Nachrichten werden kontinuierlich in einer Schleife verarbeitet, bis Ihre Aufgabe erledigt ist und setzt die Flagge. z.B. :

%Vor%

Es ist ein bisschen hacky, aber angesichts Ihrer Situation und ohne Kontrolle über den aufrufenden Code könnte es Ihre einzige Option sein, eine responsive UI zu unterhalten, ohne sofort zum Anrufer zurückzukehren.

    
Saeb Amini 23.08.2015 15:09
quelle
0

Ich habe die async / await-Kombination versucht, um diese Art von Problem zu lösen. Zuerst konvertieren wir die Sync void CanClose in async void. Dann ruft die async void-Methode die asynchrone Task-Methode auf, um die Arbeit zu erledigen. Wir müssen dies tun, weil die Gefahr von Async beim Abfangen von Ausnahmen ungültig wird.

%Vor%

Meiner Meinung nach hat die Verwendung dieses Ansatzes Vorteile:

  • leichter zu folgen und zu verstehen
  • einfachere Ausnahmebehandlung

Hinweis:

  • Ich habe das Löschungs-Token im Code-Snippet weggelassen, das Sie einfach hinzufügen können, wenn Sie möchten.
  • async / await-Schlüsselwörter existieren nach .net framework 4.5 und c # 5.0
ganagus 26.10.2017 01:03
quelle