5

次のアーキテクチャを持つWPFアプリケーションがあるとします。

[ワーカースレッド]->[キュー1]->[キューマネージャースレッド]->[キュー2]->[UIスレッド]

ワーカーはあるサービスからデータをリッスンしており、そのデータを不確定な時間(1秒間に複数回、または数秒ごとに数回)で受信し、キュー1にキューに入れ、次にキューマネージャーにキューを入れます。 UIスレッドは、データ項目がキュー2にキューイングされる速度を増減することを決定する場合があります。キュー2は、UIスレッドがUIを更新するために使用し、場合によってはUIスレッドを圧倒しないように、いくつかの項目を削除します。受信するメッセージが多すぎる(たとえば、各データメッセージのタイムスタンプをチェックし、メッセージ間の差がUIが更新した最後のデータ項目よりも少なくとも5秒古い場合にのみ、キュー2にエンキューすることを決定する場合があります)

UIスレッドには、キュー2からの新しいデータでUIを更新するためにset-intervalを起動するタイマーがあります。私ができるようにしたいのは、UIが自分自身を更新してその「応答性」を測定する速度を決定することです。スロットルするために、たとえば、UIを更新する頻度に関するタイマー間隔を増減します

UIに多数のコントロール(グリッド、チャートなど)があり、すべてがUIシェルのキュー2のデータのさまざまなフィルター処理/グループ化されたサブセットにバインドされていて、それらのコントロールを更新すると、UIが応答しなくなり、更新の合間にフリーズし始めたとします。これは、UI更新の間隔をどのように/いつ増やす/減らすかを知るためのコードからですか?基本的に、データにバインドされているすべてのコントロールでUI全体を再バインドするのにかかる時間を測定するにはどうすればよいですか?

ところで、これは良いデザインですか、それとも改善できますか?他に検討できる戦略はありますか?

4

2 に答える 2

4

2 つのスレッドがあります...メイン UI スレッド....およびレンダリング スレッド....アプリケーションの応答性を判断し、調整方法を決定するには、両方を調べる必要がある場合があります。

フレームレートを測定する

ETW イベントを監視したり、CompositionTarget.Rendering を処理して自分でフレームをカウントしたりできます....これはレンダリング スレッドを監視しています...フレームがドロップしている場合は、システムに過負荷がかかっていることがわかります...それに応じてバックグラウンド作業を抑制します。

タイマーを使用して作業をスケジュールする

@HenkHolterman で言及されているように、DispatcherTimer を使用して、Normal よりも低い優先度で実行されるようにすることで、UI スレッドの負荷を監視できます。 ..その後、バックグラウンドワーカーを解放/指示して、次の作業を行うことができます。

ただし、注意してください...タイマー間隔が小さすぎる場合...そしてシステムが過負荷になっている場合...タイマーメッセージが蓄積される可能性があります...したがって、イベントハンドラーが呼び出されたときに...作業が多すぎます (最後に呼び出されたときの記録を保持していない場合)。

UI スレッドでデリゲートを実行する

別の方法として、デリゲートを特定の優先度 (通常はバックグラウンド) で UI スレッドで実行することもできます。

Dispatcher.BeginInvoke.... を使用すると、作業がキューに入れられ、優先度の高い作業がすべて完了すると実行されます。(非同期)。

Dispatcher.Invoke を使用すると、呼び出し先の Dispatcher で優先度の高いすべての作業が完了するまで、スレッドがブロックされ、デリゲートが実行されます。

(バックグラウンド ワーカーは作業単位が終了したときにこれを行い、デリゲートはバックグラウンド ワーカーに次の作業単位を行うように指示します)。

レイテンシをいくらか取り除くことができるため、これは DispatchTimer よりも優れている可能性があります...つまり、タイマーを使用すると、タイマー間隔に依存するレイテンシが発生します。

次に、見つけたものに応じて、次の作業単位を調整できます。

さまざまなシステム パフォーマンス カウンターの監視

本当に洗練されたい場合は、メモリの使用、GC コレクションなど、アプリケーション内からさまざまなパフォーマンス カウンターを監視し、実行する作業量を動的に調整できます。

Dispatcher の背景

于 2012-08-16T19:07:22.390 に答える
2

QueueManagerとQueue2を削除するだけです。

必要なのは、数ミリ秒ごとに入力をチェックするWPFディスパッチャータイマー(Timers.Timerではない)だけです。そのタイムアウトを静的に構成できます。
次に、Queue1をブロッキングキューにして、アップストリームにあるものをすべて抑制します。

編集

ディスパッチャタイマーは、特定の優先度で開始できます。BackgroundやContextIdleなどの低いものを選択すると、GUIが過負荷になることはありません。
次に、タイマーが噛むことができる以上に噛みつかないことを確認してください。一度に1つ(またはいくつか)のアイテムのみ。この部分を調整して、GUIが可能な限り処理するが、それ以上処理しないようにします。ディスパッチャに縛られているため、ランタイム調整は無料です。

そして(必要な場合のみ) Queue1 = new BlockingCollection<MyItemType>(MaxItems) このキューがいっぱいにならないように使用できます。

于 2012-08-16T18:59:08.323 に答える