2015-09-12 5 views
9

私は賢明な人から何度もアドバイスを読んだことがありますが、それにはいくつかの注意点があります:ConfigureAwait(false)は常にの中に入れてください。だから私はその答えを知っていることはかなり確信していますが、私は100%になりたいです。このシナリオでは、他の非同期ライブラリを薄くラップするライブラリがあります。ライブラリ内のConfigureAwait(false)を呼び出したアプリケーションの同期コンテキストが失われる可能性がありますか?

ライブラリコード:

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

アプリケーションコード:

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

'ConfigureAwait'は' Task'を返しません。しかし、 'ConfiguredTaskAwaitable'があります。 'DoThingAsync'はこの例のようには見えません。 – i3arnon

+0

@ i3arnonだから私はそれが構築されていないだろうと思う。今はまし? (私のatmのコンパイラはありません) –

+0

はい、それはコンパイルされ、 'ConfigureAwait'は呼び出し側に影響しません。 – i3arnon

答えて

7

SynchronizationContextの捕捉がawaitに起こります。 ConfigureAwaitは、特定のawaitを設定します。

アプリケーションがライブラリの非同期メソッドを呼び出して待っている場合、コール内で何が起こってもSCは現場でキャプチャされます。今

、タスクを待つことに戻される前に(最初awaitの前の部分である)非同期メソッドの同期部分が実行されているので、あなたがすることができます混乱の周りにあっSynchronizationContextが、ConfigureAwaitでそれを行うことはありません。あなたの具体的な例では


あなたは非同期メソッドからConfigureAwaitの結果を返すように見えます。 ConfigureAwaitConfiguredTaskAwaitable構造体を返すため、これは起こりません。ただし、メソッドの戻り値の型を変更した場合:

public ConfiguredTaskAwaitable DoThingAsyc() 
{ 
    return otherLib.DoThingAsync().ConfigureAwait(false); 
} 

これを待っていると、実際には呼び出し元コードの動作が影響を受けます。

+0

私はあなたが間違っていると言っているわけではありませんが、この部分が混乱していることがわかります:* 'ConfigureAwait'は特定の' await' *を設定します。ライブラリの 'Task DoThingAsyc'は、タスクを返し、' await'を使用しない同期メソッドです。 'await myLib.DoThingAync();'でこのような同期メソッドを呼び出すことは、 'await otherLib.DoThingAsync() 'を直接呼び出すことと同じであるため、' ConfigureAwait(false) '*がユーザの' await'に影響を与えることを意味するはずです。 .ConfigureAwait(false); '、それは*その文言にしたがって' await'に影響を与えるべきですか? – GSerg

+3

@GSerg ['ConfigureAwait'は' Task'を返しません](http://stackoverflow.com/questions/32542861/can-configureawaitfalse-in-a-library-lose-the-synchronization-context-for-the/32543035?noredirect = 1#comment52943229_32542861) – i3arnon

2

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

から...論理的に次のコードを考えることができます:

await FooAsync(); 
RestOfMethod(); 

をこれに本質的に類似していると:

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

ましたの後ろに文脈を戻す必要があることを意味しますコール。

2

非同期コールの論理チェーンで一貫して使用されない場合、ConfigureAwait(false)は冗長コンテキストスイッチ(通常は冗長スレッドスイッチを意味します)を追加することがあります。これは、論理スタック上の一部の非同期呼び出しがConfigureAwait(false)を使用し、一部が非使用(より多くhere)の同期コンテキストが存在する場合に発生する可能性があります。

あなたはまだあなたのコード内でConfigureAwait(false)を使用する必要がありますが、あなたは、あなたが呼んでいるサードパーティのコードに覗かと、このような何かを持つ任意の矛盾を緩和することもできます。

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

これは、1つの余分なスレッドを追加します潜在的に他の多くのものを防ぐ可能性があります。また

、あなたが示したように、あなたがすべてでasync/awaitせずに、以下のようにそれを実装することが、本当に薄いラッパーを作成している場合:

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

下の例は、私の[図書館](https://github.com/tmenier/Flurl)が今日何十もの場所でそれを行うのとまったく同じです。質問の要点は'ConfigureAwait(false)'を使うようにこれらの呼び出しを変更する必要があるかどうか。 @ i3arnonが私に思い出させるように、 'ConfigureAwait'は' ConfiguredTaskAwaitable'を返します。これはおそらく*いくつかのオーバーヘッドを含んでいる 'Task'にラップする必要があります。それは、潜在的なスレッドの切り替えと一緒に言及すると、私は不思議に思っています、それは価値があるのですか? –

+1

実際、これは[新しい質問](http://stackoverflow.com/q/32551534/62600)を保証すると思います。 –

+0

@ToddMenierでは、継続コード( '/ /他のものを上にする'のような)がある場合、基本的に 'await'だけ必要です。さもなければ、あなたが特別なprorogationのasyncの意味論を特に必要としない限り、それを使用することに何の意味もありません(more [here](http://stackoverflow.com/a/21082631/1768303))。 – Noseratio

関連する問題