Basierend auf zahlreichen Büchern und Blogs einschließlich dieses ausgezeichnete hier Es ist klar, dass wenn man eine DLL-Bibliothek schreibt, die Helper-Async-Methoden, dh die Wrapper-Methoden, ausgibt, wird es allgemein als eine Best Practice angesehen, die I / O-Aufgabe von tatsächlichen Async-Methoden intern in einem Thread-Thread wie folgt abzuschließen (Pseudocode unten gezeigt) der Kürze halber und ich verwende HttpClient
als Beispiel)
Der Schlüssel hier ist die Verwendung von ConfigureAwait(false)
, so dass die IO-Task-Vervollständigung in einem Threadpool-Thread statt im ursprünglichen Threadkontext auftritt, wodurch möglicherweise Deadlocks verhindert werden.
Meine Frage ist aus der Sicht eines Anrufers. Ich bin besonders an einem Szenario interessiert, in dem es Methodenaufrufe zwischen dem Aufrufer und dem obigen Methodenaufruf gibt, wie das folgende Beispiel zeigt.
%Vor% Reicht es, ConfigureAwait(false)
nur für die finale Methode zu haben oder sollte man auch sicherstellen, dass Method1Async
und Method2Async
ihre asynchronen Methoden intern auch mit ConfigureAwait(false)
aufrufen?
Es erscheint albern, sie in all diese Zwischenmethoden einzubeziehen, insbesondere wenn Method1Async
und Method2Async
einfach Überladungen sind, die am Ende MyMethodAsync
aufrufen.
Irgendwelche Gedanken, bitte erleuchte uns!
Mit Beispiel aktualisiert Also, wenn ich eine Bibliothek mit der folgenden privaten asynchronen Methode habe,
%Vor%sollte ich sicherstellen, dass die folgenden öffentlich überladenen Methoden ConfigureAwait (false) enthalten, wie unten gezeigt?
%Vor% Definitiv nicht. ConfigureAwait
genau wie es der Name andeutet, konfiguriert await
. Es wirkt sich nur auf das await
aus, das damit verbunden ist.
ConfigureAwait
gibt tatsächlich einen anderen erwarteten Typ zurück, ConfiguredTaskAwaitable
anstelle von Task
, der wiederum einen anderen Erwartertyp ConfiguredTaskAwaiter
anstelle von TaskAwaiter
Wenn Sie die SynchronizationContext
für alle Ihre await
s ignorieren möchten, müssen Sie ConfigureAwait(false)
für jede von ihnen verwenden.
Wenn Sie die Verwendung von ConfigureAwait(false)
einschränken möchten, können Sie mein NoSynchronizationContextScope
(siehe hier ) verwenden Ganz oben:
Wenn die Aufgabe erwartet wird, erstellt sie eine entsprechende TaskAwaiter
, um die Aufgabe zu verfolgen, die auch die aktuelle SynchronizationContext
erfasst. Nachdem die Aufgabe abgeschlossen ist, führt der Erwartete den Code nach dem Warten (genannt die Fortsetzung) für diesen erfassten Kontext aus.
Sie können das verhindern, indem Sie ConfigureAwait(false)
aufrufen, was eine andere Art von unbrauchbarem ( ConfiguredTaskAwaitable
) und dessen entsprechenden Vorwarner ( ConfiguredTaskAwaitable.ConfiguredTaskAwaiter
) erzeugt, der die Fortsetzung des erfassten Kontexts nicht ausführt.
Der Punkt ist, dass für jede await
eine andere Instanz einesAwarers erstellt wird, es ist nicht etwas, das zwischen allen Erwartungswerten in der Methodeoder dem Programm geteilt wird. Es empfiehlt sich also, ConfigureAwait(false)
für jede await
-Anweisung aufzurufen.
Sie können den Quellcode für die hier sehen.
Tags und Links .net c# task-parallel-library async-await io-completion-ports