2

多くの小さなタスクの処理を行う必要がある WPF アプリケーションがあります。これらの小さなタスクはすべて同時に生成され、通常の優先度で Dispatcher Queue に追加されます。同時にビジー インジケータが表示されます。その結果、作業がタスクに分割されているにもかかわらず、実際にはビジー インジケーターがフリーズします。

これらのタスクの優先度をバックグラウンドに変更して、それが修正されたかどうかを確認しようとしましたが、それでもビジー インジケーターはフリーズしました。

イベントをサブスクライブしてDispatcher.Hooks.OperationStarted、タスクの処理中にレンダリング ジョブが発生したかどうかを確認しましたが、発生しませんでした。

何が起こっているのですか?

Observableいくつかの技術的な詳細: タスクは実際にはシーケンスからの単なるメッセージでありObserveOn(RxApp.MainThreadScheduler)ObserveOn(DispatcherScheduler). これらの各タスクの作業部分は、ObserveOn 呼び出しを介してサブスクライブするコードです。

IObservable<TaskMessage> incomingTasks;
incomingTasks.ObserveOn(RxApp.MainThreadScheduler).Subscribe(SomeMethodWhichDoesWork);

この例では、incomingTasks はおそらく 3000 以上のメッセージを短い連続で生成し、ObserveOn は SomeMethodWhichDoesWork への各呼び出しを Dispatcher キューにプッシュして、後で処理されるようにします。

4

3 に答える 3

6

基本的な問題

ビジー インジケータが停止している理由は、時間SomeMethodWhichDoesWorkがかかりすぎるためです。実行中は、Dispatcher で他の作業が行われないようにします。

アニメーションを処理するために生成された入力およびレンダリングの優先度の操作は、通常よりも低くなりますが、バックグラウンド操作よりも優先度が高くなります。ただし、Dispatcher での操作は、優先度の高い操作のエンキューによって中断されることはありません。そのため、レンダリング操作は、バックグラウンド操作であっても、実行中の操作を待機する必要があります。

DispatcherScheduler での監視に関する注意事項

ObserveOn(DispatcherScheduler)デフォルトでは、通常の優先度ですべてをプッシュします。Rx の最近のバージョンでは、優先順位を指定できるオーバーロードがオンになっています。

見落とされがちな強調すべきポイントの 1 つは、項目が次々に到着するとすぐに DispatcherScheduler によって Dispatcher のキューに入れられることです。

したがって、3000 個のアイテムがすべてかなり近くにある場合、3000 個の通常の優先度の操作が Dispatcher にバックアップされ、それらが完了するまで同じまたはそれより低い優先度のすべてをブロックします (レンダリング操作を含む)。これはほぼ間違いなくあなたが見たものです - つまり、UI 更新の負荷によっては、バックグラウンド スレッドで UI 更新作業以外のすべてを実行しても、問題が発生する可能性があることを意味します。

これに加えて、Lee が言うように、UI スレッドでサブスクリプション全体を実行していないことを確認する必要があります。私は通常、SubscribeSubscribeOn を使用するのではなく、バックグラウンド スレッドを使用するようにコードを記述しますが、これもまったく問題ありません。

推奨事項

何をするにしても、できるだけ多くの作業をバックグラウンド スレッドで行います。その点は、StackOverflow や他の場所で完全に実行されています。これをカバーするいくつかの優れたリソースを次に示します。

多数の小さな更新に直面しても UI の応答性を維持したい場合は、次のいずれかを実行できます。

  • 優先度の低いアイテムをスケジュールします。これは便利で簡単ですが、特定の優先度が必要な場合はあまり適していません
  • 更新を独自のキューに保存してキューに入れ、実行する各操作を実行させます。最後のステップとして、キューから次のアイテムを呼び出します。

全体像

少し立ち止まって全体像を見てみる価値もあります。

3000 個のアイテムを連続して UI に個別にダンプすると、ユーザーはどうなりますか? せいぜい、リフレッシュレートが100Hzのモニターを実行することになりますが、おそらくそれよりも低いでしょう. ほとんどの目的には、毎秒 10 のフレーム レートで十分すぎることがわかりました。

それだけでなく、人間は一度に 5 ~ 9 ビット以上の情報を処理することはできないと考えられています。そのため、情報を集約して表示するには、非常に多くの情報を一度に更新するよりも優れた方法が見つかるかもしれません。たとえば、一度にすべてを画面に表示するのではなく、マスター/詳細ビューを利用します。

もう 1 つのオプションは、UI の更新が引き起こしている作業量を確認することです。一部のコントロール (私は XamDataGrid を見ています) は、非常に長い測定/配置レイアウト操作を持つことができます。アニメーションを単純化できますか? より単純なビジュアル ツリーを使用しますか? 丸い点のように見える人気のビジー スピナーについて考えてみてください。実際には、色が変わっているだけです。かなり安価に達成できる素晴らしい効果。アプリケーションのプロファイリングを行って、時間の経過を確認する価値があります。

前後の全体的なアプローチについても考えます。一度に多くのアイテムを更新することが合理的に確実な場合は、それらをバッファリングしてチャンクで管理してみませんか? それはソースにまでさかのぼる利点があるかもしれません-おそらくどこかのサーバー上にありますか? いずれにせよ、Rx には、個々の項目のストリームをより大きなリストに変換できるような優れた演算子がいくつかあります。またBuffer、時間サイズで一緒にバッファリングできるオーバーロードがあります。

于 2013-11-07T13:16:54.477 に答える
0

@Pedro Pombeiroには正しい答えがあります。UI でフリーズが発生する理由は、Dispatcher で作業をキューに入れているためです。これは、作業が UI スレッドで行われることを意味します。Dispatcher は、各キューからメッセージを常に排出するメッセージ ポンプと考えることができます (優先度 [SystemIdle、ApplicationIdle、ContextIdle、Background、Input、Loaded、Render、DataBind、Normal、Send] のそれぞれについて考えることができます)。 ]))

作業を別の優先キューに入れても、同時に実行されるわけではなく、非同期で実行されます。

Rx を使用して別のスレッドで作業を実行するには、上記のように SubscribeOn を使用します。ObserveOn を使用して、UI の更新を Dispatcher にスケジュールすることを忘れないでください。

于 2013-11-06T23:47:46.833 に答える
0

.SubscribeOn(TaskPoolScheduler.TaskPool)別のスレッドでサブスクライブするために使用してみましたか?

于 2013-11-06T21:54:05.840 に答える