2016-03-29 9 views
3

Im Execute() eine Klasse, die in einer ICommand WPF Anwendung implementiert, ich habe einen externen Anruf auf ein Verfahren, das eine TaskICommand() Task.ContinueWith()

ICommand Klasse gibt:

public static void Execute(object parameter) 
{ 
    Cancel(arg1, arg2); 
} 

private static void Cancel(IList<object> arg1, object arg2) 
{ 

    Task<object> cancelTask = service.AmendAsync 
    (
     CancelTokenSource.Token, 
     object arg2 
    ); 

    ProcessCancellingResponse(arg1, arg2); 
} 

private static void ProcessCancellingResponse(IList<object> arg1, Task<object> cancelTask) 
{ 
    cancelTask.ContinueWith 
    (
     task => 
     { 
      Update(task.Result.Response); 
     }, 
     CancelTokenSource.Token, 
     TaskContinuationOptions.AttachedToParent | TaskContinuationOptions.OnlyOnRanToCompletion, 
     TaskScheduler.FromCurrentSynchronizationContext() 
    ); 
} 

Service-Klasse:

public Task<object> AmendAsync(CancellationToken cancellationToken, object arg1) 
{ 
    return Task<object>.Factory.StartNew 
    (
     () => 
     { 
      ... 
     }, 
     cancellationToken, 
     TaskCreationOptions.None, 
     TaskScheduler.Default 
    ); 
} 

Meine Fragen sind

  1. Welcher Thread ruft den ICommend Execute() auf Ist es der UI-Thread?
  2. Wird die cancelTask.ContinueWith() auf den UI-Thread oder auf einen Hintergrund Thread warten? Wenn die Task lange dauert und auf dem UI-Thread wartet, kann die Benutzeroberfläche einfrieren.
+0

die Antwort von Dudi Keleti Nach mir mehr von dem Code hinzugefügt habe, die mich um herauszufinden, führen, dass der zugrunde liegende Service-Aufruf ruft eigentlich eine Aufgabe . Factory.StartNew() –

Antwort

2

Nach Domysee Kommentar, werde ich hier klar sein, die Execute ist immer Lauf im UI-Thread, aber Sie können in dem Rückruf tun, was Sie laufen einschließlich wollen einen Hintergrund-Thread,

Über die Fortsetzung, wenn Sie ihn nicht explizit mit Thread weitermachen, wird es seine Arbeit auf TaskScheduler.Current tun, sonst wird es auf dem Scheduler fortgesetzt, den Sie definiert haben.

Auf jeden Fall prüfen verwenden async\await mit \ outer Erfassung einer Fortsetzung

await Task.Run(() =>).ConfigureAwait(true); 

await Task.Run(() =>).ConfigureAwait(false); 

aktualisiert

Laut Frage Update zu tun,

Ausführen -> UI-Thread

Abbrechen => UI-Thread

AmendAsync => Hintergrundthread

ContinueWith => UI-Thread (weil Sie FromCurrentSynchronizationContext schreiben)

+0

Angenommen, es gibt keine Logik zwischen der Execute-Methode und dem Framework (wie bei den Basisklassen RelayCommand oder DelegateCommand), hängt dies nicht von der Implementierung ab. Execute kann dann eine neue Aufgabe starten, aber sie wird über den UI-Thread aufgerufen. – Domysee

+0

Siehe überarbeitete Frage mit mehr Code hinzugefügt. –

+0

@Domysee Ich nehme nichts an;) Du hast Recht aber trotzdem kann es in jedem Thread je nach Umsetzung sein. –

1
  1. ICommand Execute(), wenn sie direkt von UI aufgerufen läuft auf Hauptthread (UI-Thread).

  2. Es hängt davon ab, wo Code ist. Wenn es sich direkt in der Execute befindet, wird es auch im Hauptthread ausgeführt, da Sie den Scheduler auf TaskScheduler.FromCurrentSynchronizationContext() festlegen.

+0

Siehe überarbeitete Frage mit mehr Code hinzugefügt. –

2

welcher Thread ruft die ICommend Execute() Ist es der UI-Thread?

Ja, es wird immer auf einem UI-Thread sein.

Wird die cancelTask.ContinueWith() auf den UI-Thread oder auf einen Hintergrund Thread warten?

ContinueWith ist nur eine regelmäßige Methodenaufruf. Es gibt keine Magie. So brechen sie:

Dieses:

private static void ProcessCancellingResponse(IList<object> arg1, Task<object> cancelTask) 
{ 
    cancelTask.ContinueWith 
    (
    task => 
    { 
     Update(task.Result.Response); 
    }, 
    CancelTokenSource.Token, 
    TaskContinuationOptions.AttachedToParent | TaskContinuationOptions.OnlyOnRanToCompletion, 
    TaskScheduler.FromCurrentSynchronizationContext() 
); 
} 

ist die gleiche wie folgt aus:

private static void ProcessCancellingResponse(IList<object> arg1, Task<object> cancelTask) 
{ 
    Action<Task> continuation = task => { Update(Task.Result.Response); }; 
    var token = CancelTokenSource.Token; 
    var options = TaskContinuationOptions.AttachedToParent | TaskContinuationOptions.OnlyOnRanToCompletion; 
    var scheduler = TaskScheduler.FromCurrentSynchronizationContext(); 
    cancelTask.ContinueWith(continuation, token, options, scheduler); 
} 

Seit ProcessCancellingResponse auf einem UI-Thread aufgerufen wird, dann wird scheduler ein Scheduler sein, der seine Aufgaben ausführt auf diesem UI-Thread. Daher wird continuation auf diesem UI-Thread ausgeführt.


Auf einer Seite zur Kenntnis, sehe ich zumindest einen Fehler: AttachedToParent an Sicherheit grenzender Wahrscheinlichkeit falsch ist. Promise-Aufgaben (asynchrone Aufgaben) sollten fast nie Aufgaben zugeordnet werden.

Die Implementierungen könnten viel sauberer sein, auch:

private static async Task ProcessCancellingResponseAsync(IList<object> arg1, Task<object> cancelTask) 
{ 
    var result = await cancelTask; 
    Update(result.Response); 
} 

public object Amend(CancellationToken cancellationToken, object arg1) 
{ 
    ... 
} 

private static void Cancel(IList<object> arg1, object arg2) 
{ 
    Task<object> cancelTask = Task.Run(() => service.Amend 
    (
    CancelTokenSource.Token, 
    object arg2 
); 

    ProcessCancellingResponse(arg1, arg2); 
} 
+0

Können Sie näher erläutern, warum Fortsetzungen nicht als 'AttachedToParent' ausgeführt werden sollten? –

+0

'AttachedToParent' ändert die Semantik der Antezedens-Aufgabe. Mit Promise-Tasks ändert sich das, was die Aufgabe bedeutet. D. h., Eine "Download" -Aufgabe wird geändert, um eine "Download- und Prozess-Aufgabe" zu bedeuten. Es ist nur ein überraschendes Verhalten für den Upstream-Code, und aus diesem Grund geben Tasks, die für die Verwendung mit asynchronem Code bestimmt sind, im Allgemeinen "DenyChildAttach" an (wodurch "AttachedToParent" überschrieben wird). –

+0

Ist 'ContinueWith' nicht an' Cancel' angehängt, weil es von 'Execute' aufgerufen wird? Die Fortsetzung folgt von der Methode, die sie aufgerufen hat, und nicht von der asynchronen Methode, die die Aufgabe zurückgibt? Daher ist AttachedToParent sinnvoll. Fehle ich etwas? –

Verwandte Themen