11

ich zum ersten Mal den Code setzen möchte und dann die Situation erklären und meine Frage basiert darauf, dass:async/warten mit ConfigureAwait des continueOnCapturedContext Parameter und SynchronizationContext für asynchrone Fortsetzungen

public partial class MainWindow : Window { 

    public MainWindow() { 
     InitializeComponent(); 
    } 

    private async void Button_Click_2(object sender, RoutedEventArgs e) { 

     var result = await GetValuesAsync(); 
     Foo.Text += result; 
    } 

    public async Task<string> GetValuesAsync() {   

     using (var httpClient = new HttpClient()) { 

      var response = await httpClient 
       .GetAsync("http://www.google.com") 
       .ConfigureAwait(continueOnCapturedContext: false); 


      // This is the continuation for the httpClient.GetAsync method. 
      // We shouldn't get back to sync context here 
      // Cuz the continueOnCapturedContext is set to *false* 
      // for the Task which is returned from httpClient.GetAsync method 
      var html = await GetStringAsync(); 

      // This is the continuation for the GetStringAsync method. 
      // Should I get back to sync context here? 
      // Cuz the continueOnCapturedContext is set to *true* 
      // for the Task which is returned from GetStringAsync 

      // However, GetStringAsync may be executed in another thread 
      // which has no knowledge for the sync context 
      // because the continueOnCapturedContext is set to *false* 
      // for the Task which is returned from httpClient.GetAsync method. 

      // But, on the other hand, GetStringAsync method also has a 
      // chance to be executed in the UI thread but we shouldn't be 
      // relying on that. 
      html += "Hey..."; 
      Foo.Text = html; 

      return html; 
     } 
    } 

    public async Task<string> GetStringAsync() { 

     await Task.Delay(1000); 
     return "Done..."; 
    } 
} 

Dies ist eine ziemlich einfache WPF Probe, die läuft auf .NET 4.5 und macht wahrscheinlich keinen großen Sinn, aber das sollte mir helfen, meine Situation zu erklären.

Ich habe eine Schaltfläche auf dem Bildschirm, die ein asynchrones Klickereignis hat. Wenn Sie sich den Code GetValuesAsync ansehen, sehen Sie zweimal die Verwendung des Schlüsselwortes await. Bei der ersten Verwendung habe ich den continueOnCapturedContext Parameter der Task.ConfigureAwait Methode auf false gesetzt. Also, dies zeigt an, dass ich nicht unbedingt meine Fortsetzung innerhalb der SynchronizationContext.Current ausgeführt werden soll. So weit, ist es gut.

Bei der zweiten await Nutzung (mit der GetStringAsync Methode), habe ich nicht die ConfigureAwait Methode aufgerufen. Also, ich habe im Grunde angegeben, dass ich möchte, um zurück zum aktuellen Synchronisationskontext für die Fortsetzung der GetStringAsync Methode zu bekommen. Wie Sie sehen können, versuche ich die Eigenschaft TextBlock.Text (die zum UI-Thread gehört) innerhalb der Fortsetzung zu setzen.

Wenn ich die Anwendung ausführen und auf die Schaltfläche klicken, erhalte ich eine Ausnahme mir die folgende Meldung geben:

Der aufrufende Thread kann nicht auf dieses Objekt, weil ein anderer Thread es besitzt.

Zuerst machte dies keinen Sinn für mich, und ich dachte, dass ich einen Fehler entdeckt, aber dann merkte ich, dass GetStringAsync kann (sehr wahrscheinlich) in einem anderen Thread ausgeführt werden, die als der UI-Thread unterschiedlich ist und haben keine Wissen für den Sync-Kontext, weil continueOnCapturedContext auf false für die Task gesetzt ist, die von httpClient.GetAsync Methode zurückgegeben wird.

Ist dies der Fall hier? Gibt es in diesem Fall auch eine Möglichkeit, dass die Methode GetStringAsync zurück an den UI-Thread gesendet wird, da die httpClient.GetAsync-Methodenfortsetzung im UI-Thread ausgeführt werden kann?

Ich habe auch ein paar Kommentare in den Code. In Anbetracht meiner Fragen und der Kommentare innerhalb des Codes, vermisse ich hier etwas?

+0

Hilft diese Antwort? http://stackoverflow.com/a/12357113/171121 –

Antwort

11

Wenn Sie ConfigureAwait(false) nennen, wird der Rest des Verfahrens auf einem der Task du bist await ing bereits abgeschlossen ist, es sei dennThread Threadpool ausgeführt werden.

Da GetAsync wird fast definitiv asynchron laufen, würde ich erwarten, GetStringAsync auf einem Thread-Pool-Thread ausgeführt werden.

public async Task<string> GetValuesAsync() {   

    using (var httpClient = new HttpClient()) { 

     var response = await httpClient 
      .GetAsync("http://www.google.com") 
      .ConfigureAwait(continueOnCapturedContext: false); 

     // And now we're on the thread pool thread. 

     // This "await" will capture the current SynchronizationContext... 
     var html = await GetStringAsync(); 
     // ... and resume it here. 

     // But it's not the UI SynchronizationContext. 
     // It's the ThreadPool SynchronizationContext. 
     // So we're back on a thread pool thread here. 

     // So this will raise an exception. 
     html += "Hey..."; 
     Foo.Text = html; 

     return html; 
    } 
} 

Auch in diesem Fall ist es eine Chance für GetStringAsync Methode bacuse die Fortsetzung httpClient.GetAsync Methode geschrieben zurück zu UI-Thread werden kann innerhalb des UI-Thread ausgeführt werden?

Der einzige Weg, GetStringAsync wird auf dem UI-Thread ausgeführt werden, wenn GetAsync abgeschlossen ist, bevor es tatsächlich ed await ist. Hoch unwahrscheinlich.

Aus diesem Grund ziehe ich ConfigureAwait(false) für jedenawait einmal der Kontext nicht mehr benötigt wird, zu verwenden.

+0

Danke! Dein letzter Satz ist die Art, wie ich folge (ich bin gewesen, aber jetzt werde ich mehr darauf bestehen). Das wird keinen merklichen Unterschied machen, aber die Verwendung von 'ConfigureAwait (false)' wird uns auch vor unnötigen 'SynchronizationContext'-Prüfungen schützen, wenn es nicht benötigt wird. – tugberk

+0

"Sehr unwahrscheinlich" ist natürlich nicht genug. Entweder muss es 100% sein oder Ihr Code muss beide Fälle tolerieren. – usr

+1

stimme ich im Allgemeinen zu. Deshalb bevorzuge ich 'ConfigureAwait (false)' für jeden 'wait', sobald der Kontext nicht mehr benötigt wird. –

Verwandte Themen