汎用ライブラリでは、現在のSynchronizationContextを続けるのを避けるために、すべての待機呼び出しでConfigureAwait(false)
を使用することがよく知られています。非同期ライブラリのベストプラクティス:ConfigureAwait(false)と同期コンテキストの設定との比較
ConfigureAwait(false)
でコードベース全体をペーストする代わりに、publicサーフェスメソッドでSynchronizationContextをnullに設定し、ユーザーに返す前に復元することができます。言い換えれば:
public async Task SomeSurfaceMethod()
{
var callerSyncCtx = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(null);
try
{
// Do work
}
finally
{
SynchronizationContext.SetSynchronizationContext(callerSyncCtx);
}
}
これはまた読みやすくするためusing
に包ますることができます。
このアプローチには欠点がありますか?同じ結果が得られませんか?
大きな利点は、すべてのConfigureAwait(false)
コールの可読性が明らかです。また、分析者がこれを緩和しても、開発者はSynchronizationContextの変更を忘れることができると主張することもできますが、ConfigureAwait(false)
を忘れる可能性があります。
いくつかのエキゾチックな利点は、SynchronizationContextをキャプチャするかどうかの選択をすべてのメソッドに埋め込むことではありません。つまり、あるケースでは、メソッドXをSynchronizationContextで実行したい場合がありますが、別のメソッドでは、メソッドなしで同じメソッドを実行したいことがあります。 ConfigureAwait(false)
が埋め込まれている場合は不可能です。確かに、これは非常にまれな要件ですが、Npgsql(この質問のトリガ)で作業している間、私はそれにぶつかりました。
そして、 'SomeSurfaceMethod(keepInContext bool){aswait asyncMethod()、ConfigureAwait(keepInContext)}'は単純で、呼び出し元に選択させることができます... – jlvaquero
@jlvaquero問題は、 'asyncMethod'内で* 'ConfigureAwait'も指定するので、' keepInContext'をスタック経由で全部渡す必要があります。表面的なレベルでSynchronizationContextを1回だけ設定するのと比べてかなり控えめだと思われます... –
これは読みやすさを追加しますが、当初はそうではないと主張します。コードベースの初心者にとっては、 'ConfigureAwait'は、ライブラリコードを書くときに学んだことです。あなたの質問にまだ答えはありませんが、私はそれについて考え続けます。 –