2017-12-30 40 views
0

Ich verstehe, dass das Blockieren auf Async-Code in den meisten Fällen abgelehnt wird.Blockieren auf async mit Thread.Sleep() & Task.Yield()

Ich habe jedoch eine WPF-Anwendung, die eine Hintergrundaufgabe ausführt. Wenn der Benutzer das Fenster schließt, sollte diese Aufgabe abgebrochen und das Schließen des Fensters verzögert werden, bis die Löschung und anschließende Bereinigung abgeschlossen ist. In der Zwischenzeit sollten weitere Benutzereingaben blockiert werden.

Nach ein bisschen & Suche versucht, kam ich mit dem folgenden Code auf:

ViewModel.cs:

async void RunTaskAsync() 
{ 
    _cancelTknSource = new CancellationTokenSource(); 
    try { 
     await Task.Run(/*...long running calculation...*/, _cancelTknSource.Token); 
    } 
    catch (OperationCanceledException) { } 
    finally { 
     /*...cleanup...*/ 
     _cancelTknSource.Dispose(); 
     _cancelTknSource = null;    
    } 
} 

async Task CancelAsync() 
{ 
    _cancelTknSource?.Cancel(); 
    while (_cancelTknSource != null) { 
     Thread.Sleep(10); 
     await Task.Yield(); //prevents dead-lock 
    } 
} 

Window.xaml.cs:

async void Window_Closing(object sender, CancelEventArgs e) 
{ 
    e.Cancel = true; 
    await DataContext.CancelAsync(); 
    Close(); 
} 

(Alle Diese Methoden werden vom UI-Thread aufgerufen.

Ist dies eine gute Vorgehensweise? Wenn nicht, was sind die Alternativen?

+2

Willkommen gut Sir. Ich frage mich, ob diese Frage vielleicht besser auf der Geschwisterseite _Code Review_ gestellt wird (weil Ihre eher eine Design- als eine Problemfrage ist) im StackExchange-Netzwerk? Viel Glück – MickyD

+1

Task.Yield() nicht Deadlock beheben, löst es nicht das grundlegende Problem, dass der UI-Thread nicht mehr in der Lage ist, Benachrichtigungen zu verarbeiten. Der Hinweis für WPF unterscheidet sich nicht grundlegend von Winforms (https://Stackoverflow.com/a/1732361/17034). –

+0

"Warten auf Task.Run (/ * ... lang laufende Berechnung ... * /, _ cancelTknSource.Token);" wenn Sie auf diese Aufgabe warten. Was ist der Sinn des Tokens? – efekctive

Antwort

0

Ohne Fehlerbehandlung und Bereinigung (z. B. _cancelTknSource und _runningTask auf null setzen) sollte der folgende Code tun. Sie müssen jedoch noch einige UI-Funktionen zwischen Window_Closing und dem tatsächlichen Schließen des Fensters blockieren.

async void RunTaskAsync() 
{ 
    using(_cancelTknSource = new CancellationTokenSource()) 
    { 
     _runningTask =Task.Run(/*...long running calculation...*/, _cancelTknSource.Token); 
     await _runningTask; 
    } 
} 

Task CancelAsync() 
{ 
    _cancelTknSource?.Cancel(); 
    return _runningTask; 
} 
Window.xaml.cs: 

async void Window_Closing(object sender, CancelEventArgs e) 
{ 
    e.Cancel = true; 
    await DataContext.CancelAsync(); 
    Close(); 
} 
+0

Für mich ist es nicht offensichtlich, ob Close() vor oder nach dem Rest von RunTaskAsync nach dem using-Block ausgeführt wird (wo ich meine Bereinigung durchführen möchte) - da beide Methoden die gleiche Aufgabe erwarten ... Vielleicht besser ändere RunTaskAsync() von void in Task und speichere es in _runningTask (wie Peter vorgeschlagen hat) – Cornix

+0

@Cornix bezüglich mehrerer 'hits auf dieselbe Aufgabe siehe [Was passiert, wenn ich auf eine bereits laufende oder laufende Aufgabe warte?] (https: //stackoverflow.com/a/32434481/585968) – MickyD

Verwandte Themen