2017-05-19 5 views
2

This answer sagtWarum Sync-Kontext funktioniert nicht für warten?

standardmäßig wird der await Bediener den aktuellen „Kontext“ erfassen und dass die Asynchron-Methode wieder aufnehmen verwenden.

ich diesen Code in meiner Konsole app bin versucht:

static void Main(string[] args) 
{ 
    Test().Wait(); 
} 

private static async Task Test() 
{ 
    var context = new SynchronizationContext(); 
    SynchronizationContext.SetSynchronizationContext(context); 
    Console.WriteLine("Thread before: " + Thread.CurrentThread.ManagedThreadId); 
    Console.WriteLine(await GetResultAsync()); 
    Console.WriteLine("Thread after: " + Thread.CurrentThread.ManagedThreadId); 
} 

private static async Task<string> GetResultAsync() 
{ 
    return await Task.Factory.StartNew(() => 
    { 
     Console.WriteLine("Thread inside: " + Thread.CurrentThread.ManagedThreadId); 
     return "Hello stackoverflow!"; 
    }); 
} 

... und raus:

Thread before: 1 
Thread inside: 3 
Hello stackoverflow! 
Thread after: 3 

Warum? Und wie sollte ich den Sync-Kontext einstellen, wenn ich nach dem Warten den gleichen Thread verwenden möchte?

+0

Sie verwenden den Begriff Kontext hier explizit, weil nicht alle Kontexte an einen einzelnen Thread gebunden sind. In einer Konsolenanwendung gibt es keine integrierten Gründe, einen Thread vor anderen zu bevorzugen (im Gegensatz zu UI-Frameworks mit einem UI-Thread). –

Antwort

4

Warum?

new SynchronizationContext()by convention ist das gleiche wie ein nullSynchronizationContext. Sowohl "no SyncCtx" als auch "default SyncCtx" stellen die Arbeit in den Thread-Pool.

Es gibt keine 1: 1-Beziehung zwischen SynchronizationContext und einem bestimmten Thread. Beispiel:

  • Die WinForms-Benutzeroberfläche SynchronizationContext wird die Arbeit in einem einzelnen UI-Thread in die Warteschlange stellen. AFAIK, das WinForms SynchronizationContext ist ein Singleton, also gibt es ein 1: 1 Mapping in diesem Fall.
  • Die WPF SynchronizationContext wird in der Warteschlange arbeiten . Bei der letzten Überprüfung erstellt WPF für jedes Fenster der obersten Ebene eine neue Instanz, auch wenn alle denselben Thread verwenden. Also gibt es ein N: 1 Mapping.
  • Der Thread-Pool (Standard/null) SynchronizationContext kann Arbeit in einem beliebigen Thread-Thread Thread Warteschlange.Wenn Sie keine standardmäßige SynchronizationContext-Instanz erstellen, gibt es eine 1: N-Zuordnung.

Und auch, wie ich Sync-Kontext setzen soll, wenn ich den gleichen Thread nach await verwenden?

Sie müssen eine benutzerdefinierte SynchronizationContext verwenden. Ich empfehle die Verwendung meiner AsyncContext or AsyncContextThread types, denn das ist nicht einfach Code zu schreiben.

+0

In WPF '' SynchronizationContext.Current' 'ist eigentlich nie das Gleiche nach 'warten', wo die Aufgabe asynchron abgeschlossen wird. Das liegt daran, dass "Dispatcher" jeden Callback für eine eindeutige Instanz von "DispatcherSynchronizationContext" ausführt. Irgendwie kontrovers mit 'continueOnCapturedContext' Argument von' ConfigureAwait', IMO. – Noseratio

+0

Seltsam! Dieses Verhalten hat sich vor ein paar Jahren verändert. –

1

Ich bin kein Experte zu diesem Thema, ich habe gerade ein paar Tutorials gelesen.

Code nach await wird als Taskfortsetzung mit erfasstem Synchronisationskontext ausgeführt. Sie haben new SynchronizationContext() angegeben, das ThreadPool verwendet, um diesen Code auszuführen.

Quellcode: link

Blick auf Send Methode in SynchronizationContext Klasse:

public virtual void Post(SendOrPostCallback d, Object state) 
{ 
    ThreadPool.QueueUserWorkItem(new WaitCallback(d), state); 
} 

So wie Sie sehen, wird die Fortsetzung nicht auf main Thread ausgeführt werden. Es verwendet ThreadPool.

Wenn GetResultAsync() erfolgt, thread 3 ist kostenlos zu benutzen, und wird sofort von ThreadPool.QueueUserWorkItem in SynchronizationContext wiederverwendet.

Sie müssen also Ihren eigenen Synchronisationskontext für die Konsolenanwendung erstellen.

Habe nicht gelesen, aber vielleicht hilft das link.

Verwandte Themen