2015-09-12 2 views
9

Ich habe den Rat viele Male von Leuten gelesen, die klüger sind als ich, und es hat wenige Vorbehalte: Verwenden Sie immer ConfigureAwait(false) innerhalb der Bibliothek Code. Ich bin mir ziemlich sicher, dass ich die Antwort kenne, aber ich möchte 100% sein. Das Szenario ist Ich habe eine Bibliothek, die einige andere asynchrone Bibliothek dünn umschließt.Kann ConfigureAwait (false) in einer Bibliothek den Synchronisierungskontext für die aufrufende Anwendung verlieren?

Bibliothek Code:

public async Task DoThingAsyc() { 
    // do some setup 
    return await otherLib.DoThingAsync().ConfigureAwait(false); 
} 

Anwendungscode:

// need to preserve my synchronization context 
await myLib.DoThingAync(); 
// do I have my context here or did my lib lose it? 
+1

'ConfigureAwait' kehrt nicht ein' Task' , aber ein 'ConfiguredTaskAwaitable'. 'DoThingAsync' kann nicht so aussehen wie im Beispiel. – i3arnon

+0

@ i3arnon also ich denke, das hätte nicht einmal gebaut. Besser jetzt? (Kein Compiler auf mir atm) –

+0

Ja, das würde kompilieren und die 'ConfigureAwait' würde den Anrufer nicht beeinflussen. – i3arnon

Antwort

7

Nr

Die Erfassung des SynchronizationContext auf await geschieht. ConfigureAwait konfiguriert die spezifischen await.

Wenn die Anwendung eine Async-Methode einer Bibliothek aufruft und sie erwartet, wird der SC an Ort und Stelle erfasst, unabhängig davon, was innerhalb des Aufrufs geschieht.

Nun, da das synchrone Teil des Asynchron-Verfahrens (das ist der Teil vor den ersten await ist) ausgeführt wird, bevor eine Aufgabe abgewartet werden zurückgegeben, können Sie Kampf mit den SynchronizationContext da, aber ConfigureAwait tut das nicht.


In Ihrem speziellen Beispiel scheinen Sie das Ergebnis ConfigureAwait von der Asynchron-Methode zurückkehrt zu werden. Das kann nicht passieren, weil ConfigureAwait die Struktur ConfiguredTaskAwaitable zurückgibt. Wenn wir jedoch den Methodenrückgabetyp ändern:

Dann wird erwartet, dass es in der Tat das abwarten Verhalten des aufrufenden Codes beeinflussen wird.

+0

Ich sage nicht, dass du falsch liegst, aber ich finde diesen Teil verwirrend: * 'ConfigureAwait' konfiguriert das spezifische' warten' *. Die 'Task DoThingAsyc' der Bibliothek ist eine synchrone Methode, die eine Aufgabe zurückgibt und nicht 'wartet' verwendet. Sollte dies dann bedeuten, dass 'ConfigureAwait (false)' * den 'gave' des Benutzers beeinflusst, weil das Aufrufen einer solchen synchronen Methode mit 'awar myLib.DoThingAync();' nicht anders ist als den direkten Aufruf von 'awaitotherLib.DoThingAsync() .ConfigureAwait (false); 'was * sollte * das' erwarten' gemäß dieser Formulierung beeinflussen? – GSerg

+3

@GSerg ['ConfigureAwait' gibt keine' Task' zurück (http://stackoverflow.com/questions/32542861/can-configureawaitfalse-in-a-library-lose-the-synchronization-context-for-the/32543035? Noredirect = 1 # comment52943229_32542861) – i3arnon

2

Beispiel von http://blogs.msdn.com/b/pfxteam/archive/2012/01/20/10259049.aspx

... logisch können Sie aus dem folgenden Code denken:

await FooAsync(); 
RestOfMethod(); 

wie in der Natur zu diesem ähnlich ist:

var t = FooAsync(); 
var currentContext = SynchronizationContext.Current; 
t.ContinueWith(delegate 
{ 
    if (currentContext == null) 
     RestOfMethod(); 
    else 
     currentContext.Post(delegate { RestOfMethod(); }, null); 
}, TaskScheduler.Current); 

die bedeutet, dass Sie Ihren Kontext nach derzurück haben solltenAnruf.

2

Wenn ConfigureAwait(false) inkonsistent in der logischen Kette von Async-Aufrufen verwendet wird, können redundante Kontext-Switches hinzugefügt werden (was normalerweise redundante Thread-Switches bedeutet). Dies kann bei Vorhandensein eines Synchronisationskontextes passieren, wenn einige asynchrone Aufrufe des logischen Stapels ConfigureAwait(false) verwenden und einige nicht (mehr here).

Sie sollten noch ConfigureAwait(false) in Ihrem Code verwenden, aber Sie können in die 3rd-Party-Code spähen möchten Sie anrufen und mildern Unstimmigkeiten mit etwas wie folgt aus:

public async Task DoThingAsyc() { 
    // do some setup 
    await Task.Run(() => otherLib.DoThingAsync()).ConfigureAwait(false); 
    // do some other stuff 
} 

Dies würde einen zusätzlichen Thread hinzufügen wechseln, aber möglicherweise viele andere verhindern.

Außerdem, wenn Sie eine wirklich dünne Hülle sind die Schaffung wie Sie zeigte, können Sie es wie unten zu implementieren, ohne async/await überhaupt:

public Task DoThingAsyc() { 
    // do some setup 
    return otherLib.DoThingAsync(); 
} 
+1

Ihr Beispiel unten ist genau wie meine [Bibliothek] (https://github.com/tmenier/Flurl) es in Dutzenden von Orten heute macht, und der Kern der Frage ist ob oder nicht sollte ich diese Anrufe ändern, um 'ConfigureAwait (false)' zu verwenden. Wie @i3arnon mich daran erinnerte, gibt "ConfigureAwait" eine 'ConfiguredTaskAwaitable' zurück, die ich in eine' Task' zurückverwandeln müsste, die vermutlich * einen * Overhead mit sich bringt. Das zusammen mit der möglichen Thread-Umschaltung, die du erwähnst, lässt mich fragen, ob es das wert ist oder sollte ich es einfach so lassen, wie es ist? –

+1

Eigentlich glaube ich, dass dies eine [neue Frage] garantiert (http://stackoverflow.com/q/32551534/62600). –

+0

@ToddMenier, im Grunde brauchen Sie nur "erwarten" wenn Sie einen Fortsetzungscode haben Andernfalls ist es sinnlos, sie zu verwenden, es sei denn, Sie möchten ausdrücklich die asynchrone Semantik der Ausnahmeversetzung (mehr [hier] (http://stackoverflow.com/a/21082631/1768303)). – Noseratio

Verwandte Themen