41

Stephen Toub のブログ

SynchronizationContext と TaskScheduler はどちらも「スケジューラ」を表す抽象化であり、何らかの作業を与えるものであり、いつどこでその作業を実行するかを決定します。スケジューラにはさまざまな形式があります。たとえば、ThreadPool はスケジューラです。ThreadPool.QueueUserWorkItem を呼び出して実行するデリゲートを指定すると、そのデリゲートがキューに入れられ、最終的に ThreadPool のスレッドの 1 つがそのデリゲートを取得して実行します。ユーザー インターフェイスには、メッセージ ポンプというスケジューラもあります。

そのため、Reactive Extensions のDispatcherThreadPoolTaskSchedulerSystem.Reactive.Concurrency.EventLoopScheduler、 SynchronizationContext 、およびIScheduler実装はすべて、その意味での「スケジューラ」です。

それらの違いは何ですか?

なぜそれらすべてが必要だったのですか?EventLoop、Dispatcher、ThreadPool を取得すると思います。IScheduler もよく説明されています。
しかし、TaskScheduler と SynchronizationContext はまだ明確ではありません。

Stephen Cleary の優れた記事でSynchronizationContext が説明されており、理解できたと思います。なぜ TaskScheduler が必要なのかは明らかではありません。

説明するか、情報源を示してください。

4

3 に答える 3

22

私はCLR via C#ジェフリー・リッチャーの本を読んでいたところです。彼のおかげで、そのトピックに関連する簡単な説明をすることもできます. (回答の詳細全体に完全に同意していないと仮定します)

まず第一に、TaskSchedulerオブジェクトはスケジュールされたタスクの実行を担当します。FCLには、スレッド プール タスク スケジューラ同期コンテキスト タスク スケジューラTaskSchedulerの 2 つの派生タイプが付属しています。デフォルトでは、すべてのアプリケーションがスレッド プール タスク スケジューラを使用します。このタスク スケジューラは、タスクをスレッド プールのワーカー スレッドにスケジュールします。の静的プロパティを照会することで、既定のタスク スケジューラへの参照を取得できます。TaskSchedulerDefault

同期コンテキスト タスク スケジューラは、通常、グラフィカル ユーザー インターフェイスを備えたアプリケーションに使用されます。このタスク スケジューラは、すべてのタスクをアプリケーションの GUI スレッドにスケジュールして、すべてのタスク コードがボタンやメニュー項目などの UI コンポーネントを正常に更新できるようにします。同期コンテキスト タスク スケジューラは、スレッド プールをまったく使用しません。TaskSchedulerの静的FromCurrentSynchronizationContextメソッドを照会することにより、同期コンテキスト タスク スケジューラへの参照を取得できます。

実装からわかるようSynchronizationContextTaskSchedulerに、内部的にSynchronizationContextフィールドを使用します。は、これらすべての問題を解決するFCLと呼ばれる基本クラスを定義します。System.Threading.SynchronizationContext

  • GUI アプリケーションは、UI 要素を作成したスレッドがその UI 要素を更新できる唯一のスレッドであるというスレッド モデルを強制します。スレッド プール スレッドを介して UI 要素を更新しようとすると、コードが例外をスローするため、これは問題です。どういうわけか、スレッド プール スレッドは GUI スレッドに UI 要素を更新させる必要があります。
  • ASP.NET アプリケーションでは、任意のスレッドが必要なことを実行できます。スレッド プール スレッドがクライアントの要求の処理を開始すると、クライアントのカルチャを想定できるため、Web サーバーはカルチャ固有の形式で数値、日付、および時刻を返すことができます。さらに、Web サーバーはクライアントの ID を引き受けることができるため、サーバーは、クライアントがアクセスを許可されているリソースにのみアクセスできます。スレッド プール スレッドが非同期操作を生成すると、非同期操作の結果を処理する別のスレッド プール スレッドによって完了される場合があります。この作業は元のクライアント要求に代わって実行されますが、カルチャと ID は新しいスレッド プール スレッドに「流れる」必要があるため、クライアントに代わって実行される追加の作業は、クライアントのカルチャと ID 情報を使用して実行されます。

簡単に言うと、派生SynchronizationContextオブジェクトはアプリケーション モデルをそのスレッド モデルに接続します。FCL は SynchronizationContext から派生したいくつかのクラスを定義しますが、通常、これらのクラスを直接扱うことはありません。実際、それらの多くは公開されておらず、文書化されていません。

SynchronizationContextほとんどの場合、アプリケーション開発者はクラスについて何も知る必要はありません。を実行awaitするTaskと、呼び出しスレッドのSynchronizationContext オブジェクトが取得されます。スレッド プールのスレッドが を完了するTaskと、SynchronizationContext オブジェクトが使用され、アプリケーション モデルに適切なスレッド モデルが確保されます。そのため、GUI スレッド awaitsが の場合Task、オペレーターに続くコードawaitは GUI スレッドでも実行されることが保証され、そのコードが UI 要素を更新できるようになります。ASP.NET アプリケーションの場合、await 演算子に続くコードは、クライアントのカルチャとプリンシパル情報が関連付けられているスレッド プール スレッドで実行されることが保証されます

TaskSchedulerもちろん、特別なタスク スケジューリングが必要な場合は、から派生した独自のクラスを定義できます。Microsoft は、タスクのサンプル コードを多数提供しており、Parallel Extensions Extras パッケージに多数のタスク スケジューラのソース コードを含めています。のようIOTaskSchedulerに、、、、、、。LimitedConcurrencyLevelTaskScheduler_OrderedTaskSchedulerPrioritizingTaskSchedulerThreadPerTaskScheduler

于 2019-03-10T07:23:33.197 に答える
16

各プラットフォームには独自の「スケジューラ」があり、その周りに独自の抽象化があります。たとえば、WinForms はメッセージ ポンプを使用します。WPF は、「Dispatcher」内で抽象化された別のメッセージ ポンプを使用します。ThreadPool は、「ThreadPool」内で抽象化された別の「スケジューラ」です。これら (およびその他のいくつか) は、下位レベルのスケジューラです。

Task と TaskScheduler は、Task のユーザーがこれらの下位レベルでタスクのスケジューリングについて考える必要がないようにしたいと考えています (もちろん、抽象化された方法で可能です)。タスクを開始できる必要があり、周囲の「スケジューラ」がそれを処理する必要があります。たとえば、TaskFactory.StartNew(()=>{LengthyOperation()})実行しているプラ​​ットフォームに関係なく動作するはずです。ここで a のSynchronizationContext出番です。現在実行中のフレームワークに関与する下位レベルのスケジューラを認識します。これは に渡され、TaskSchedulerそのスケジューラは、現在実行中のフレームワークに関連付けられた下位レベルのスケジューラを介して、タスクのスケジュール (おそらく ThreadPool への) と継続のスケジュールの両方を行うことができます (SynchronizationContext) 同期要件を維持します。たとえば、タスクを ThreadPool で実行したいが、継続を UI スレッドで実行したい場合があります。

TaskSchedulerは他の複数のスケジューラの抽象化であることを知っておくことが重要です。これが存在する唯一の理由ではなく、この「余分な」抽象化の理由の 1 つです。

于 2013-04-15T17:14:22.070 に答える
11

とはいえ、引用したとおり、

SynchronizationContext と TaskScheduler はどちらも「スケジューラ」を表す抽象化です</p>

IMO、抽象化の程度(したがってAPI)が異なります。SynchronizationContext は、Post/Send が単純なメソッド デリゲートを受け取るという意味で、より一般的な API です。

一方、TaskScheduler は TPL 固有の抽象化であるため、Taskオブジェクトを処理する QueueTask などのメソッドを提供します。タスク スケジューラの代わりに同期コンテキストを使用する (つまり、SynchronizationContext の TPL 固有の実装を使用する) と、タスク スケジューリングの操作がより面倒になります (もちろん、TPL のコンテキストでは弱い型付けの API になります)。そのため、TPL 設計者は、TPL にとって意味のある抽象スケジューラ API をモデル化することを選択しました (それが抽象化の目的です - ですよね?) - もちろん、ギャップを埋めるために、FCL には、SynchronizationContextTaskSchedulerSynchronizationContext に対するラッパー TaskScheduler 実装である内部クラスが含まれています。

SynchronizationContext は .NET 2.0 で導入され、TPL は .NET 4 で導入されました。シーケンスが逆の場合、つまり .NET 2.0 の時点で TPL が存在していたとしたら、FCL 設計者がどのような選択をしたかを考えるのは興味深いことです。IMO、TaskScheduler は、デリゲートを特定の特殊化のタスクとしてモデル化することにより、SynchrinizationContext の代わりに使用できました。

于 2012-03-06T09:31:27.780 に答える