2015-08-26 9 views
20

Ich habe gelesen this question von Noseratio, die ein Verhalten zeigt, wo TaskScheduler.Current ist nicht die gleiche nach einem erwarteten hat seinen Betrieb beendet.Warten auf den Kontext nach dem asynchronen Vorgang nicht fortgesetzt?

Die Antwort lautet:

Wenn es keine eigentliche Aufgabe ist ausgeführt wird, dann TaskScheduler.Current ist die gleiche wie TaskScheduler.Default

was wahr ist. Ich sah es schon here:

  • TaskScheduler.Default
    • Gibt eine Instanz des ThreadPoolTaskScheduler
  • TaskScheduler.Current
    • Wenn innerhalb einer Ausführung Task aufgerufen wird wieder die TaskScheduler von die derzeit Aufgabe kehrt die Ausführung
    • Wenn von einem anderen Ort namens TaskScheduler.Default

Aber dann dachte ich, Wenn ja, lassen Sie uns tun erstellen eine tatsächliche Task (und nicht nur Task.Yield()) und testen Sie es:

Erste Messagebox ist "True", se cond ist "False"

Frage:

Wie Sie sehen können, habe ich eine tatsächliche Aufgabe erstellt.

Ich kann verstehen, warum die erste MessageBox True Ausbeute. Das ist becuase der:

Wenn innerhalb einer Ausführung Task aufgerufen wird die Taskscheduler der gerade ausgeführten Aufgabe

Und diese Aufgabe zurückkehren ts hat, die die TaskScheduler.FromCurrentSynchronizationContext()

gesendet wird, aber warum der zusammenhang ist nicht bei der zweiten MessageBox erhalten? Für mich war es aus Stephan's Antwort nicht klar.

Zusätzliche Informationen:

Wenn ich stattdessen schreiben (der zweiten messagebox):

MessageBox.Show((TaskScheduler.Current == TaskScheduler.Default).ToString()); 

Es true nicht ergeben. Aber warum ?

+0

Ausgezeichnete Frage, aber was, wenn 'Task-Scheduler! = Synchronisationskontext '? – AgentFire

+0

Ein guter Fall für die harte Regel: nimm niemals 'TaskScheduler.Current' ist, was du denkst :) Es ist nur garantiert, was du an 'Factory.StartNew' für den Umfang der Aufgabenaktion lambda weitergegeben hast (was in deinem Fall' Func 'ist und zurückkommt, wenn es den ersten' trifft warte '). Jedes andere Verhalten sollte als Implementierungsdetails behandelt werden. – Noseratio

Antwort

10

Die Gründe für Verwirrung sind diese:

  1. Die Benutzeroberfläche hat keine „speziellen“ TaskScheduler. Der Standardfall für Code auf dem UI-Thread ausgeführt wird, ist, dass TaskScheduler.Current speichert die ThreadPoolTaskScheduler und SynchronizationContext.Current speichern WindowsFormsSynchronizationContext (oder die relevanteste in anderen UI-Anwendungen)
  2. ThreadPoolTaskScheduler in TaskScheduler.Current bedeutet nicht unbedingt, es ist die TaskScheduler verwendet wird, um den Strom zu laufen Stück Code. Es bedeutet auch TaskSchdeuler.Current == TaskScheduler.Default und so "es wird kein TaskScheduler verwendet".
  3. TaskScheduler.FromCurrentSynchronizationContext() gibt kein "acutal" TaskScheduler zurück. Es gibt einen "Proxy" zurück, der Aufgaben direkt an die erfassten SynchronizationContext sendet.

Also, wenn Sie führen Sie Ihren Test vor der Aufgabe starten (oder an einem anderen Ort) finden Sie das gleiche Ergebnis wie nach dem await erhalten:

MessageBox.Show(TaskScheduler.Current == TaskScheduler.FromCurrentSynchronizationContext()); // False 

Da TaskScheduler.CurrentThreadPoolTaskScheduler und TaskScheduler.FromCurrentSynchronizationContext() gibt eine SynchronizationContextTaskScheduler zurück.

Dies ist der Fluss von Ihrem Beispiel:

  • Sie einen neuen SynchronizationContextTaskScheduler aus der SynchronizationContext (das heißt WindowsFormsSynchronizationContext) UI erstellen.
  • Planen Sie die Aufgabe, die Sie mit Task.Factory.StartNew auf diesem TaskScheduler erstellen. Da es sich nur um einen "Proxy" handelt, wird der Delegat an die Adresse WindowsFormsSynchronizationContext gesendet, die ihn im UI-Thread aufruft.
  • Der synchrone Teil (der Teil vor der ersten Wartezeit) dieser Methode wird im UI-Thread ausgeführt, während er der SynchronizationContextTaskScheduler zugeordnet ist.
  • Die Methode erreicht die Erwartung und ist "suspended" während der Erfassung WindowsFormsSynchronizationContext.
  • Wenn die Fortsetzung nach der Wartezeit fortgesetzt wird, wird sie an die WindowsFormsSynchronizationContext und nicht an die SynchronizationContextTaskScheduler gesendet, da SynchronizationContext s Vorrang haben (this can be seen in Task.SetContinuationForAwait). Es dann läuft regelmäßig auf dem UI-Thread, ohne "spezielle" TaskScheduler so.

So läuft das erstellte Aufgabe auf dem Proxy-TaskScheduler denen die SynchronizationContext verwendet, aber die Fortsetzung nach dem await auf diese SynchronizationContext und nicht die TaskScheduler geschrieben.

+0

Die Fortsetzung nach dem 'await' läuft auf dem' ThreadPoolTaskScheduler', nicht auf der Benutzeroberfläche. –

+0

@YuvalItzchakov Warum sagst du das? Ich bin nicht einverstanden. Es wird mit seinem 'SynchronizationContext' auf die Benutzeroberfläche gepostet. – i3arnon

+0

Warte, welche 'erwarten' sprichst du? :) –

Verwandte Themen