11

私は現在、全体でTAPを使用する大部分が非同期のアプリケーションに取り組んでいます。Tasksを生成するためのメソッドを持つすべてのクラスには、sがTaskScheduler注入されています。これにより、タスクの明示的なスケジューリングを実行できます。これは、私が理解しているように、MicrosoftがAsyncCTPを使用する方法ではありません。

新しいアプローチ(暗黙的なスケジューリング)で私が抱えている唯一の問題は、以前の哲学は常に「継続によって常にタスクスケジューラが指定されることを知っているので、タスクを完了するコンテキストについて心配する必要がない」ということです。 。

微妙なスレッドエラーを回避するという点で非常にうまく機能しているという理由だけで、それから離れることは少し心配です。コードのすべてのビットについて、コーダーが自分がどのスレッドを使用しているかを考慮することを覚えていることがわかります。彼らがタスクスケジューラを指定しなかった場合、それはバグです。

質問1:暗黙のアプローチが良い考えであることを誰かが私に安心させることができますか?ConfigureAwait(false)と、レガシー/サードパーティのコードでの明示的なスケジューリングによって、非常に多くの問題が発生しているのがわかります。たとえば、「await-ridden」コードが常にUIスレッドで実行されていることを確認するにはどうすればよいですか?

質問2:では、コードからすべてのDIを削除し、暗黙的なスケジューリングの使用を開始すると仮定するTaskSchedulerと、デフォルトのタスクスケジューラをどのように設定しますか?メソッドの途中で、高価なメソッドを待つ直前にスケジューラーを変更し、その後再び設定するのはどうですか?

(ps私はすでにhttp://msmvps.com/blogs/jon_skeet/archive/2010/11/02/configuring-waiting.aspxを読んでいます)

4

1 に答える 1

10

答えてみます。;)

質問1:暗黙のアプローチが良い考えであることを誰かが私に安心させることができますか?ConfigureAwait(false)と、レガシー/サードパーティのコードでの明示的なスケジューリングによって、非常に多くの問題が発生しているのがわかります。たとえば、「await-ridden」コードが常にUIスレッドで実行されていることを確認するにはどうすればよいですか?

のルールConfigureAwait(false)は非常に単純です。メソッドの残りの部分をスレッドプールで実行できる場合は使用し、メソッドの残りの部分を特定のコンテキスト(UIコンテキストなど)で実行する必要がある場合は使用しないでください。

一般的に、ConfigureAwait(false)UIレイヤーコード(MVVMのViewModelsなどのUIタイプレイヤーを含む)ではなく、ライブラリコードで使用する必要があります。メソッドが部分的にバックグラウンド計算と部分的にUI更新である場合は、2つのメソッドに分割する必要があります。

質問2:では、コードからすべてのTaskScheduler DIを削除し、暗黙的なスケジューリングの使用を開始すると仮定すると、デフォルトのタスクスケジューラをどのように設定しますか?

async/await通常は使用しませんTaskScheduler; それらは「スケジューリングコンテキスト」の概念を使用します。これは実際にはであり、がない場合にのみSynchronizationContext.Currentフォールバックします。したがって、独自のスケジューラを置き換えるには、を使用して行うことができます。この件に関するMSDNの記事で詳細を読むことができます。TaskScheduler.CurrentSynchronizationContextSynchronizationContext.SetSynchronizationContextSynchronizationContext

デフォルトのスケジューリングコンテキストは、ほとんど常に必要なものである必要があります。つまり、それをいじる必要はありません。単体テストを行うとき、またはコンソールプログラム/Win32サービスの場合にのみ変更します。

メソッドの途中で、高価なメソッドを待つ直前にスケジューラーを変更し、その後再び設定するのはどうですか?

高価な操作(おそらくスレッドプールで)を実行したい場合はawait、の結果TaskEx.Run

他の理由(同時実行など)でスケジューラーを変更する場合はawait、の結果TaskFactory.StartNew

どちらの場合も、メソッド(またはデリゲート)は他のスケジューラーで実行され、その後、メソッドの残りの部分は通常のコンテキストで再開されます。

理想的には、各asyncメソッドが単一の実行コンテキスト内に存在するようにします。異なるコンテキストを必要とするメソッドの異なる部分がある場合は、それらを異なるメソッドに分割します。このルールの唯一の例外はConfigureAwait(false)、メソッドが任意のコンテキストで開始し、残りの実行のためにスレッドプールコンテキストに戻ることを許可することです。ConfigureAwait(false)設計哲学としてではなく、最適化(ライブラリコードではデフォルトでオンになっている)と見なす必要があります。

これが私の「ThreadisDead」トークからのいくつかのポイントで、あなたのデザインに役立つと思います。

  • タスクベースの非同期パターンのガイドラインに従ってください。
  • コードベースがより非同期になると、(従来のオブジェクト指向とは対照的に)本質的に機能的になります。これは正常であり、受け入れる必要があります。
  • コードベースがより非同期になるにつれて、共有メモリの同時実行性は徐々にメッセージパッシングの同時実行性に進化します(つまり、ConcurrentExclusiveSchedulerPairは新しいReaderWriterLockです)。
于 2012-01-06T17:43:37.133 に答える